<?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[Crafting software]]></title><description><![CDATA[Talking about how to craft software]]></description><link>https://emmanuelvalverderamos.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!pJED!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg</url><title>Crafting software</title><link>https://emmanuelvalverderamos.substack.com</link></image><generator>Substack</generator><lastBuildDate>Fri, 21 Aug 2026 01:08:02 GMT</lastBuildDate><atom:link href="https://emmanuelvalverderamos.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Emmanuel Valverde Ramos]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[emmanuelvalverderamos@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[emmanuelvalverderamos@substack.com]]></itunes:email><itunes:name><![CDATA[Emmanuel Valverde Ramos]]></itunes:name></itunes:owner><itunes:author><![CDATA[Emmanuel Valverde Ramos]]></itunes:author><googleplay:owner><![CDATA[emmanuelvalverderamos@substack.com]]></googleplay:owner><googleplay:email><![CDATA[emmanuelvalverderamos@substack.com]]></googleplay:email><googleplay:author><![CDATA[Emmanuel Valverde Ramos]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[The productivity trap]]></title><description><![CDATA[Productivity, effectiveness, and efficiency are not the same thing.]]></description><link>https://emmanuelvalverderamos.substack.com/p/the-productivity-trap</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/the-productivity-trap</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 18 Aug 2026 06:00:58 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7EP-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdded3eba-b05d-468a-99be-b3b902431b54_1916x821.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine a restaurant on a busy Friday night. The kitchen is moving fast. Dishes are leaving the counter. Waiters are crossing the room with plates in both hands. Tickets are being completed. From the outside, it looks productive. A lot of work is happening, and a lot of output is being produced.</p><p>But then you look closer. Some dishes are going to the wrong tables. Some customers are receiving meals they did not order. Some plates look beautiful, but the food is cold. Other dishes are correct and customers are happy, but every plate takes twenty steps, three people, two corrections, and a small argument near the oven.</p><p>That restaurant does not have one problem. It has three different questions tangled together.</p><p>It may be productive because it produces many dishes. It may be ineffective because some dishes do not satisfy the right customers. It may be inefficient because even the good results require too much waste, friction, coordination, and rework.</p><p>Software teams live inside the same confusion. We say a team is productive because it closes tickets, merges pull requests, ships features, fixes bugs, writes tests, creates dashboards, or deploys often. Sometimes that is true. Sometimes it is only motion wearing the costume of progress.</p><p>The hard part is not producing software. The hard part is producing the right software, in the right amount, with a cost of change the organization can keep carrying.</p><h2>TL;DR</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7EP-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdded3eba-b05d-468a-99be-b3b902431b54_1916x821.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7EP-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdded3eba-b05d-468a-99be-b3b902431b54_1916x821.png 424w, https://substackcdn.com/image/fetch/$s_!7EP-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdded3eba-b05d-468a-99be-b3b902431b54_1916x821.png 848w, https://substackcdn.com/image/fetch/$s_!7EP-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdded3eba-b05d-468a-99be-b3b902431b54_1916x821.png 1272w, https://substackcdn.com/image/fetch/$s_!7EP-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdded3eba-b05d-468a-99be-b3b902431b54_1916x821.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7EP-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdded3eba-b05d-468a-99be-b3b902431b54_1916x821.png" width="1456" height="624" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dded3eba-b05d-468a-99be-b3b902431b54_1916x821.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:624,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1321428,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdded3eba-b05d-468a-99be-b3b902431b54_1916x821.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_!7EP-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdded3eba-b05d-468a-99be-b3b902431b54_1916x821.png 424w, https://substackcdn.com/image/fetch/$s_!7EP-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdded3eba-b05d-468a-99be-b3b902431b54_1916x821.png 848w, https://substackcdn.com/image/fetch/$s_!7EP-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdded3eba-b05d-468a-99be-b3b902431b54_1916x821.png 1272w, https://substackcdn.com/image/fetch/$s_!7EP-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdded3eba-b05d-468a-99be-b3b902431b54_1916x821.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Productivity, effectiveness, and efficiency are related, but they are not interchangeable.</p><p>Productivity asks: <strong>what did we produce?</strong></p><p>Effectiveness asks: <strong>did the work achieve the right result?</strong></p><p>Efficiency asks: <strong>did we use our resources well while doing it?</strong></p><p>In software and product development, confusing these three ideas creates bad decisions. Productivity without effectiveness creates useless output. Effectiveness without efficiency creates expensive progress. Efficiency without effectiveness creates polished waste.</p><p>The best teams do not start by asking how to produce more. They ask whether the work matters, how much waste exists in the way they work, and only then how to increase the flow of valuable output.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Index</h2><ol><li><p>Why this distinction matters in software</p></li><li><p>Productivity: what work was produced?</p></li><li><p>Effectiveness: did the work matter?</p></li><li><p>Efficiency: what did the work cost?</p></li><li><p>How the three ideas are related</p></li><li><p>Output, outcome, throughput, and the missing confusion</p></li><li><p>The first trap: productive but ineffective</p></li><li><p>The second trap: effective but inefficient</p></li><li><p>The third trap: efficient but ineffective</p></li><li><p>Examples from software engineering</p></li><li><p>How teams should use these words</p></li><li><p>Conclusions</p></li><li><p>References</p></li></ol><h2>Why this distinction matters in software</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2xxp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c1e57cf-ed1b-4c0b-a019-d5ed48a4419a_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2xxp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c1e57cf-ed1b-4c0b-a019-d5ed48a4419a_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!2xxp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c1e57cf-ed1b-4c0b-a019-d5ed48a4419a_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!2xxp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c1e57cf-ed1b-4c0b-a019-d5ed48a4419a_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!2xxp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c1e57cf-ed1b-4c0b-a019-d5ed48a4419a_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2xxp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c1e57cf-ed1b-4c0b-a019-d5ed48a4419a_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c1e57cf-ed1b-4c0b-a019-d5ed48a4419a_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1005089,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c1e57cf-ed1b-4c0b-a019-d5ed48a4419a_1024x558.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_!2xxp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c1e57cf-ed1b-4c0b-a019-d5ed48a4419a_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!2xxp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c1e57cf-ed1b-4c0b-a019-d5ed48a4419a_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!2xxp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c1e57cf-ed1b-4c0b-a019-d5ed48a4419a_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!2xxp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c1e57cf-ed1b-4c0b-a019-d5ed48a4419a_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Software has a strange property: producing more of it is not automatically good. More code can mean more capability, but it can also mean more maintenance. More features can mean more customer value, but they can also mean more confusion. More tests can mean more confidence, but they can also mean more brittleness. More automation can mean faster feedback, but it can also mean more machinery to understand.</p><p>This is why the word &#8220;productivity&#8221; is dangerous when it is used alone. It sounds positive. It sounds objective. It sounds managerial. But without a clear object, it hides more than it explains.</p><p>A developer can look productive by pushing a lot of code. A team can look productive by closing many tickets. A department can look productive by shipping many initiatives. A company can look productive by filling a roadmap with completed items. None of that proves that the product improved, that customers received value, that risk went down, or that the system became easier to change.</p><p>Software work is not valuable because it exists. It is valuable when it changes something that matters.</p><p>That is the center of the distinction. Productivity is about output. Effectiveness is about outcome. Efficiency is about the cost of getting there. A mature team needs all three, but each one answers a different question.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qbB3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec751e55-832b-4854-b929-14faea28d0fb_3551x652.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qbB3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec751e55-832b-4854-b929-14faea28d0fb_3551x652.png 424w, https://substackcdn.com/image/fetch/$s_!qbB3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec751e55-832b-4854-b929-14faea28d0fb_3551x652.png 848w, https://substackcdn.com/image/fetch/$s_!qbB3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec751e55-832b-4854-b929-14faea28d0fb_3551x652.png 1272w, https://substackcdn.com/image/fetch/$s_!qbB3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec751e55-832b-4854-b929-14faea28d0fb_3551x652.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qbB3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec751e55-832b-4854-b929-14faea28d0fb_3551x652.png" width="1456" height="267" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ec751e55-832b-4854-b929-14faea28d0fb_3551x652.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:267,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:135966,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec751e55-832b-4854-b929-14faea28d0fb_3551x652.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_!qbB3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec751e55-832b-4854-b929-14faea28d0fb_3551x652.png 424w, https://substackcdn.com/image/fetch/$s_!qbB3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec751e55-832b-4854-b929-14faea28d0fb_3551x652.png 848w, https://substackcdn.com/image/fetch/$s_!qbB3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec751e55-832b-4854-b929-14faea28d0fb_3551x652.png 1272w, https://substackcdn.com/image/fetch/$s_!qbB3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec751e55-832b-4854-b929-14faea28d0fb_3551x652.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The diagram matters because the same piece of work can be judged three different ways. A feature can be productive because it was shipped, ineffective because it solved the wrong problem, and inefficient because it left behind unnecessary complexity. One activity. Three questions. Three different judgments.</p><h2>Productivity: what work was produced?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6Lgs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362fd5e7-3d9f-4ed9-ac90-742dd9966b92_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6Lgs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362fd5e7-3d9f-4ed9-ac90-742dd9966b92_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!6Lgs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362fd5e7-3d9f-4ed9-ac90-742dd9966b92_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!6Lgs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362fd5e7-3d9f-4ed9-ac90-742dd9966b92_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!6Lgs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362fd5e7-3d9f-4ed9-ac90-742dd9966b92_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6Lgs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362fd5e7-3d9f-4ed9-ac90-742dd9966b92_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/362fd5e7-3d9f-4ed9-ac90-742dd9966b92_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:941901,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362fd5e7-3d9f-4ed9-ac90-742dd9966b92_1024x558.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_!6Lgs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362fd5e7-3d9f-4ed9-ac90-742dd9966b92_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!6Lgs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362fd5e7-3d9f-4ed9-ac90-742dd9966b92_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!6Lgs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362fd5e7-3d9f-4ed9-ac90-742dd9966b92_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!6Lgs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362fd5e7-3d9f-4ed9-ac90-742dd9966b92_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Productivity is the easiest of the three to see because it leaves visible traces. A pull request was merged. A ticket was closed. A feature was released. A bug was fixed. A test was added. A deployment happened. A migration was completed. A document was written.</p><p>There is nothing wrong with caring about this. A team that produces nothing is not secretly excellent. Software development must produce artifacts, decisions, learning, working behavior, and operational improvements. Output matters because without output there is no movement through the system.</p><p>The problem starts when productivity is treated as if it already included value. It does not.</p><p>A team can produce ten features that customers do not use. It can fix twenty bugs while ignoring the one problem that damages trust every week. It can merge forty pull requests that make the codebase harder to understand. It can add hundreds of tests that protect implementation details but fail to protect meaningful behavior.</p><p>Productivity tells us that work happened. It does not tell us whether the work was worth doing.</p><p>That is why &#8220;this team is productive&#8221; is an incomplete sentence. Productive at what? Producing code? Producing features? Producing learning? Producing business outcomes? Producing operational stability? Producing safer change? Producing options for the future?</p><p>The object matters. In software, different forms of output have different economic consequences. A line of code is not just a line of code. It is something that may need to be read, tested, reviewed, debugged, secured, deployed, observed, explained, migrated, and eventually removed.</p><p>Productivity can also be confused with the rate at which work is completed. A team might produce more work in the same period of time, and that may be useful. But even then, the same warning applies: producing more finished work does not prove that the finished work mattered.</p><p>This is the first aha moment: in software, output is not the end of work. Output is the beginning of responsibility.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Q1qF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c592c88-3ba5-45a4-a080-d1e75fd5aeed_2555x732.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Q1qF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c592c88-3ba5-45a4-a080-d1e75fd5aeed_2555x732.png 424w, https://substackcdn.com/image/fetch/$s_!Q1qF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c592c88-3ba5-45a4-a080-d1e75fd5aeed_2555x732.png 848w, https://substackcdn.com/image/fetch/$s_!Q1qF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c592c88-3ba5-45a4-a080-d1e75fd5aeed_2555x732.png 1272w, https://substackcdn.com/image/fetch/$s_!Q1qF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c592c88-3ba5-45a4-a080-d1e75fd5aeed_2555x732.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Q1qF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c592c88-3ba5-45a4-a080-d1e75fd5aeed_2555x732.png" width="1456" height="417" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6c592c88-3ba5-45a4-a080-d1e75fd5aeed_2555x732.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:417,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:106342,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c592c88-3ba5-45a4-a080-d1e75fd5aeed_2555x732.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_!Q1qF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c592c88-3ba5-45a4-a080-d1e75fd5aeed_2555x732.png 424w, https://substackcdn.com/image/fetch/$s_!Q1qF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c592c88-3ba5-45a4-a080-d1e75fd5aeed_2555x732.png 848w, https://substackcdn.com/image/fetch/$s_!Q1qF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c592c88-3ba5-45a4-a080-d1e75fd5aeed_2555x732.png 1272w, https://substackcdn.com/image/fetch/$s_!Q1qF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c592c88-3ba5-45a4-a080-d1e75fd5aeed_2555x732.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is why output needs judgment. Useful output becomes an investment. Useless output becomes inventory. Both may look like productivity on a dashboard, but only one improves the system.</p><h2>Effectiveness: did the work matter?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oWCl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5872d9c-e35c-4d4a-9797-6ed4cf3c0cf2_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oWCl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5872d9c-e35c-4d4a-9797-6ed4cf3c0cf2_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!oWCl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5872d9c-e35c-4d4a-9797-6ed4cf3c0cf2_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!oWCl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5872d9c-e35c-4d4a-9797-6ed4cf3c0cf2_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!oWCl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5872d9c-e35c-4d4a-9797-6ed4cf3c0cf2_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oWCl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5872d9c-e35c-4d4a-9797-6ed4cf3c0cf2_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b5872d9c-e35c-4d4a-9797-6ed4cf3c0cf2_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:733934,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5872d9c-e35c-4d4a-9797-6ed4cf3c0cf2_1024x558.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_!oWCl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5872d9c-e35c-4d4a-9797-6ed4cf3c0cf2_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!oWCl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5872d9c-e35c-4d4a-9797-6ed4cf3c0cf2_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!oWCl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5872d9c-e35c-4d4a-9797-6ed4cf3c0cf2_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!oWCl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5872d9c-e35c-4d4a-9797-6ed4cf3c0cf2_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Effectiveness asks a harder question: did the work achieve the right result?</p><p>A team may ship a feature exactly as requested. The user interface works. The acceptance criteria pass. The release goes out on time. Everyone can honestly say the work was completed. But if users do not need it, if the business problem remains, or if the feature solves a symptom while leaving the real pain untouched, the team was productive but ineffective.</p><p>Effectiveness is not about whether effort was spent. It is about whether the work changed reality in the intended direction.</p><p>This matters deeply in product development. Product work is not the act of filling a backlog with ideas. It is not translating stakeholder requests into tickets. It is not making roadmaps look organized. Product work is economic judgment under uncertainty. It asks what deserves to exist, why now, for whom, at what cost, with what expected return, and with what opportunity cost.</p><p>A customer may ask for a dashboard, but the real need may be faster decision-making. A stakeholder may ask for export functionality, but the real need may be trust in the data. A support team may ask for more configuration, but the real need may be clearer rules and fewer exceptions. A sales team may ask for a custom workflow, but the real question may be whether this customer segment should shape the product at all.</p><p>Effectiveness forces teams to move from &#8220;we built the requested thing&#8221; to &#8220;we improved the situation that made the request appear.&#8221;</p><p>That shift is small in wording and huge in practice. The first version celebrates delivery. The second version asks whether delivery mattered.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!993U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3917f35-2034-4778-8293-9110db2216ae_2916x396.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!993U!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3917f35-2034-4778-8293-9110db2216ae_2916x396.png 424w, https://substackcdn.com/image/fetch/$s_!993U!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3917f35-2034-4778-8293-9110db2216ae_2916x396.png 848w, https://substackcdn.com/image/fetch/$s_!993U!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3917f35-2034-4778-8293-9110db2216ae_2916x396.png 1272w, https://substackcdn.com/image/fetch/$s_!993U!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3917f35-2034-4778-8293-9110db2216ae_2916x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!993U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3917f35-2034-4778-8293-9110db2216ae_2916x396.png" width="1456" height="198" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b3917f35-2034-4778-8293-9110db2216ae_2916x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:198,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:78810,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3917f35-2034-4778-8293-9110db2216ae_2916x396.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_!993U!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3917f35-2034-4778-8293-9110db2216ae_2916x396.png 424w, https://substackcdn.com/image/fetch/$s_!993U!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3917f35-2034-4778-8293-9110db2216ae_2916x396.png 848w, https://substackcdn.com/image/fetch/$s_!993U!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3917f35-2034-4778-8293-9110db2216ae_2916x396.png 1272w, https://substackcdn.com/image/fetch/$s_!993U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3917f35-2034-4778-8293-9110db2216ae_2916x396.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The important point is not that requests are bad. Many requests contain useful information. The problem appears when a request becomes work without passing through a product judgment. Building what was asked is not the same as improving the situation that made the request appear.</p><h2>Efficiency: what did the work cost?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Qi_V!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad49974d-956d-474f-a390-426f9318a5bd_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Qi_V!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad49974d-956d-474f-a390-426f9318a5bd_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!Qi_V!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad49974d-956d-474f-a390-426f9318a5bd_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!Qi_V!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad49974d-956d-474f-a390-426f9318a5bd_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!Qi_V!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad49974d-956d-474f-a390-426f9318a5bd_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Qi_V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad49974d-956d-474f-a390-426f9318a5bd_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ad49974d-956d-474f-a390-426f9318a5bd_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:682731,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad49974d-956d-474f-a390-426f9318a5bd_1024x558.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_!Qi_V!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad49974d-956d-474f-a390-426f9318a5bd_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!Qi_V!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad49974d-956d-474f-a390-426f9318a5bd_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!Qi_V!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad49974d-956d-474f-a390-426f9318a5bd_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!Qi_V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad49974d-956d-474f-a390-426f9318a5bd_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Efficiency asks a different question: did we use our resources well while producing the result?</p><p>In software, resources are not only time and money. They include attention, coordination, review capacity, test capacity, deployment capacity, operational capacity, emotional energy, and cognitive load. They also include future capacity, because every decision changes what the team can afford to do next.</p><p>A team can be effective and still inefficient. This is common in legacy systems. The team knows the right problem. The product opportunity is real. Customers care. The business case is sound. But every change is painful because the system is hard to understand, hard to test, hard to deploy, and hard to modify safely.</p><p>The direction is right, but the cost of movement is too high.</p><p>That cost matters because it compounds. If every valuable change requires heroic effort, the organization slowly becomes afraid of its own product. Good ideas become expensive. Small changes become political. Releases become stressful. Refactoring becomes hard to justify because everyone is already late. Testing becomes slower because the system was not designed for feedback. Eventually, the team still wants to improve the product, but the codebase charges a toll on every step.</p><p>Efficiency is not about squeezing people to do more with less. That is a shallow and often harmful reading. In a healthy engineering system, efficiency means reducing waste from the path of valuable work.</p><p>Waiting is waste. Rework is waste. Unclear requirements are waste. Slow feedback is waste. Fragile tests are waste. Long-lived branches are waste. Manual release rituals are waste when they could be safely automated. Relearning the same confusing module every month is waste. Creating work that another team must untangle later is waste.</p><p>Efficiency also does not mean keeping everyone fully utilized. A system can be so full that it stops moving. A road with every lane occupied is not necessarily efficient if no car can move. A team with every person overloaded is not necessarily productive if work cannot reach users.</p><p>That is the paradox: what looks efficient in a static snapshot can be destructive in a dynamic system. Full calendars, full queues, and full workloads may look responsible, but if they create waiting, task switching, congestion, and delayed delivery, they are not efficiency. They are gridlock.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!K0WH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c138391-4d31-458e-97ee-1cf9f5b0d6aa_1322x1324.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!K0WH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c138391-4d31-458e-97ee-1cf9f5b0d6aa_1322x1324.png 424w, https://substackcdn.com/image/fetch/$s_!K0WH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c138391-4d31-458e-97ee-1cf9f5b0d6aa_1322x1324.png 848w, https://substackcdn.com/image/fetch/$s_!K0WH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c138391-4d31-458e-97ee-1cf9f5b0d6aa_1322x1324.png 1272w, https://substackcdn.com/image/fetch/$s_!K0WH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c138391-4d31-458e-97ee-1cf9f5b0d6aa_1322x1324.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!K0WH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c138391-4d31-458e-97ee-1cf9f5b0d6aa_1322x1324.png" width="1322" height="1324" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4c138391-4d31-458e-97ee-1cf9f5b0d6aa_1322x1324.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1324,&quot;width&quot;:1322,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:106290,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c138391-4d31-458e-97ee-1cf9f5b0d6aa_1322x1324.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_!K0WH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c138391-4d31-458e-97ee-1cf9f5b0d6aa_1322x1324.png 424w, https://substackcdn.com/image/fetch/$s_!K0WH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c138391-4d31-458e-97ee-1cf9f5b0d6aa_1322x1324.png 848w, https://substackcdn.com/image/fetch/$s_!K0WH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c138391-4d31-458e-97ee-1cf9f5b0d6aa_1322x1324.png 1272w, https://substackcdn.com/image/fetch/$s_!K0WH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c138391-4d31-458e-97ee-1cf9f5b0d6aa_1322x1324.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Efficiency is about protecting the organization&#8217;s ability to move. It is not a photograph of busy people. It is the health of the path from need to outcome.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;f32da054-88c4-4af3-beda-1820d2bf45e7&quot;,&quot;caption&quot;:&quot;Modern organizations often praise busyness as if it were the clearest sign of effectiveness. A team with no idle time appears disciplined, efficient, and productive. A room full of people constantly moving, answering, processing, and multitasking seems, at first glance, like a healthy system. Yet the core argument of&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;The Resource Utilization Trap: Why Keeping Everyone Busy Can Destroy Real Productivity&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-05-19T05:00:58.445Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!XHGN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/the-resource-utilization-trap-why&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:189661244,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>How the three ideas are related</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!30gt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c802fc-2bc5-4df8-a001-4ad458087446_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!30gt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c802fc-2bc5-4df8-a001-4ad458087446_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!30gt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c802fc-2bc5-4df8-a001-4ad458087446_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!30gt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c802fc-2bc5-4df8-a001-4ad458087446_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!30gt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c802fc-2bc5-4df8-a001-4ad458087446_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!30gt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c802fc-2bc5-4df8-a001-4ad458087446_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/47c802fc-2bc5-4df8-a001-4ad458087446_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:841763,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c802fc-2bc5-4df8-a001-4ad458087446_1024x558.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_!30gt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c802fc-2bc5-4df8-a001-4ad458087446_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!30gt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c802fc-2bc5-4df8-a001-4ad458087446_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!30gt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c802fc-2bc5-4df8-a001-4ad458087446_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!30gt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c802fc-2bc5-4df8-a001-4ad458087446_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The three ideas are related because software work has direction, production, and cost.</p><p>Effectiveness protects direction. It asks whether the work should exist and whether it changes the right thing.</p><p>Efficiency protects capacity. It asks whether the way of working wastes time, attention, coordination, energy, or future changeability.</p><p>Productivity protects output. It asks whether the team is producing enough work once the direction is sound and the system is not drowning in waste.</p><p>The order matters. If a team starts with productivity, it may produce more of the wrong thing. If it starts with efficiency, it may optimize the delivery of low-value work. If it starts with effectiveness, it first asks whether the work deserves to consume capacity at all.</p><p>A strong operating sequence looks like this:</p><ol><li><p><strong>Effectiveness first:</strong> should we do this, and what result should change?</p></li><li><p><strong>Efficiency second:</strong> what is the smallest responsible way to create or test that result with less waste?</p></li><li><p><strong>Productivity third:</strong> once the work is valuable and the path is healthy, how do we increase the flow of aligned output?</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZC9p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F247f7476-0985-48f4-b29c-f73ae708bca2_4885x1100.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZC9p!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F247f7476-0985-48f4-b29c-f73ae708bca2_4885x1100.png 424w, https://substackcdn.com/image/fetch/$s_!ZC9p!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F247f7476-0985-48f4-b29c-f73ae708bca2_4885x1100.png 848w, https://substackcdn.com/image/fetch/$s_!ZC9p!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F247f7476-0985-48f4-b29c-f73ae708bca2_4885x1100.png 1272w, https://substackcdn.com/image/fetch/$s_!ZC9p!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F247f7476-0985-48f4-b29c-f73ae708bca2_4885x1100.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZC9p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F247f7476-0985-48f4-b29c-f73ae708bca2_4885x1100.png" width="1456" height="328" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/247f7476-0985-48f4-b29c-f73ae708bca2_4885x1100.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:328,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:232144,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F247f7476-0985-48f4-b29c-f73ae708bca2_4885x1100.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_!ZC9p!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F247f7476-0985-48f4-b29c-f73ae708bca2_4885x1100.png 424w, https://substackcdn.com/image/fetch/$s_!ZC9p!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F247f7476-0985-48f4-b29c-f73ae708bca2_4885x1100.png 848w, https://substackcdn.com/image/fetch/$s_!ZC9p!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F247f7476-0985-48f4-b29c-f73ae708bca2_4885x1100.png 1272w, https://substackcdn.com/image/fetch/$s_!ZC9p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F247f7476-0985-48f4-b29c-f73ae708bca2_4885x1100.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>This sequence is not bureaucracy. It is economic discipline. If direction is wrong, more productivity scales the mistake. If the path is wasteful, more productivity burns capacity faster. If both direction and operating model are sound, productivity becomes powerful.</p><h2>Output, outcome, throughput, and the missing confusion</h2><p>There is a related distinction that helps sharpen the argument without replacing it: output, outcome, and throughput.</p><p>Output is what the team ships. Outcome is what changes because of that work. Throughput is how much finished work the system can complete over time. These ideas are not the same as productivity, effectiveness, and efficiency, but they help explain why teams confuse them.</p><p>Productivity often shows up as output or throughput. A team shipped five features. A team closed thirty work items. A team delivered more changes this month than last month. That may tell us something useful about production and flow, but it does not prove value.</p><p>Effectiveness is closer to outcome. It asks what changed because the work entered the world. Did users complete onboarding more easily? Did support pain decrease? Did a risky manual process become safer? Did the product become more useful? Did the business problem actually improve?</p><p>Efficiency is related to flow, but it is not the same thing as keeping everyone busy. A team can have high utilization and poor flow. Everyone may be working, switching, replying, reviewing, and attending meetings, while valuable work moves slowly through the system. In that case, the team is not efficient in the sense that matters. It is simply saturated.</p><p>This connection matters because teams often use a metric from one category as if it answered another category&#8217;s question. They treat shipped features as proof of value. They treat busy people as proof of efficiency. They treat faster delivery as proof of better product judgment.</p><p>None of those conclusions necessarily follows.</p><p>A better mapping is this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XcFP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faad3bee0-8513-4760-b5b3-b0fe0501bbd9_1348x560.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XcFP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faad3bee0-8513-4760-b5b3-b0fe0501bbd9_1348x560.png 424w, https://substackcdn.com/image/fetch/$s_!XcFP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faad3bee0-8513-4760-b5b3-b0fe0501bbd9_1348x560.png 848w, https://substackcdn.com/image/fetch/$s_!XcFP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faad3bee0-8513-4760-b5b3-b0fe0501bbd9_1348x560.png 1272w, https://substackcdn.com/image/fetch/$s_!XcFP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faad3bee0-8513-4760-b5b3-b0fe0501bbd9_1348x560.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XcFP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faad3bee0-8513-4760-b5b3-b0fe0501bbd9_1348x560.png" width="1348" height="560" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aad3bee0-8513-4760-b5b3-b0fe0501bbd9_1348x560.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:560,&quot;width&quot;:1348,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:88985,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ca61fea-8f90-46cc-b4a7-8c13ba709b2c_1404x692.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_!XcFP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faad3bee0-8513-4760-b5b3-b0fe0501bbd9_1348x560.png 424w, https://substackcdn.com/image/fetch/$s_!XcFP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faad3bee0-8513-4760-b5b3-b0fe0501bbd9_1348x560.png 848w, https://substackcdn.com/image/fetch/$s_!XcFP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faad3bee0-8513-4760-b5b3-b0fe0501bbd9_1348x560.png 1272w, https://substackcdn.com/image/fetch/$s_!XcFP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faad3bee0-8513-4760-b5b3-b0fe0501bbd9_1348x560.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This keeps the focus where it belongs. The article is not about creating a bigger vocabulary. It is about separating questions that teams often collapse into one vague compliment: &#8220;we are productive.&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2CPp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db7dfe6-8b84-4833-9837-05d3da0fc3a7_1685x1516.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2CPp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db7dfe6-8b84-4833-9837-05d3da0fc3a7_1685x1516.png 424w, https://substackcdn.com/image/fetch/$s_!2CPp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db7dfe6-8b84-4833-9837-05d3da0fc3a7_1685x1516.png 848w, https://substackcdn.com/image/fetch/$s_!2CPp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db7dfe6-8b84-4833-9837-05d3da0fc3a7_1685x1516.png 1272w, https://substackcdn.com/image/fetch/$s_!2CPp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db7dfe6-8b84-4833-9837-05d3da0fc3a7_1685x1516.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2CPp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db7dfe6-8b84-4833-9837-05d3da0fc3a7_1685x1516.png" width="1456" height="1310" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1db7dfe6-8b84-4833-9837-05d3da0fc3a7_1685x1516.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1310,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131828,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db7dfe6-8b84-4833-9837-05d3da0fc3a7_1685x1516.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_!2CPp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db7dfe6-8b84-4833-9837-05d3da0fc3a7_1685x1516.png 424w, https://substackcdn.com/image/fetch/$s_!2CPp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db7dfe6-8b84-4833-9837-05d3da0fc3a7_1685x1516.png 848w, https://substackcdn.com/image/fetch/$s_!2CPp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db7dfe6-8b84-4833-9837-05d3da0fc3a7_1685x1516.png 1272w, https://substackcdn.com/image/fetch/$s_!2CPp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1db7dfe6-8b84-4833-9837-05d3da0fc3a7_1685x1516.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The phrase &#8220;we are productive&#8221; becomes useful only after the team clarifies which question it is answering. Without that clarification, it becomes a fog machine. It fills the room, but it does not help anyone see.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;0e2bb53d-154a-4f5c-80d9-f038588ddcfd&quot;,&quot;caption&quot;:&quot;&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Output, Outcome, and Throughput: What Product Teams are really measuring when they say they deliver value&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-05-12T05:01:35.830Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/vimeo/w_728,c_limit,d_video_placeholder.png/206617354&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/output-outcome-and-throughput-what&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:189660243,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>The order of things matters</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5XPK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5309c929-9174-4503-9607-c08bcc53cbf3_1024x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5XPK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5309c929-9174-4503-9607-c08bcc53cbf3_1024x572.png 424w, https://substackcdn.com/image/fetch/$s_!5XPK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5309c929-9174-4503-9607-c08bcc53cbf3_1024x572.png 848w, https://substackcdn.com/image/fetch/$s_!5XPK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5309c929-9174-4503-9607-c08bcc53cbf3_1024x572.png 1272w, https://substackcdn.com/image/fetch/$s_!5XPK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5309c929-9174-4503-9607-c08bcc53cbf3_1024x572.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5XPK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5309c929-9174-4503-9607-c08bcc53cbf3_1024x572.png" width="1024" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5309c929-9174-4503-9607-c08bcc53cbf3_1024x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1218489,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5309c929-9174-4503-9607-c08bcc53cbf3_1024x572.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_!5XPK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5309c929-9174-4503-9607-c08bcc53cbf3_1024x572.png 424w, https://substackcdn.com/image/fetch/$s_!5XPK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5309c929-9174-4503-9607-c08bcc53cbf3_1024x572.png 848w, https://substackcdn.com/image/fetch/$s_!5XPK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5309c929-9174-4503-9607-c08bcc53cbf3_1024x572.png 1272w, https://substackcdn.com/image/fetch/$s_!5XPK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5309c929-9174-4503-9607-c08bcc53cbf3_1024x572.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The order matters because productivity, effectiveness, and efficiency do not have the same responsibility in the system.</p><p>Effectiveness decides direction. Efficiency protects capacity. Productivity increases output. If we change the order, we change the kind of mistake the team is likely to scale.</p><p>Starting with productivity sounds tempting because output is visible. It gives managers something to count, teams something to report, and organizations something to celebrate. More tickets closed. More pull requests merged. More features shipped. More deployments completed. But if the work is not effective, increasing productivity only produces more of the wrong thing.</p><p>That is how teams create impressive movement without meaningful progress. They become faster at adding features nobody needs, fixing symptoms instead of causes, automating workflows that should not exist, and producing code the product will carry for years. Productivity is powerful only after direction is clear. Before that, it is an amplifier. It amplifies value when the work matters. It amplifies waste when the work does not.</p><p>Starting with efficiency has a different failure mode. It can make the organization feel mature because everything looks smoother. Meetings are shorter. Pipelines are faster. Boards are cleaner. Handoffs are reduced. People seem better coordinated. But if the team has not asked whether the work should exist, efficiency simply makes the wrong work cheaper to produce.</p><p>That is polished waste. The system becomes elegant, but not necessarily useful. The team removes friction from a path that may be leading to the wrong destination. A fast delivery system is valuable when product judgment is sound. When judgment is weak, speed becomes a way to move mistakes into production with less resistance.</p><p>Starting with effectiveness changes the conversation. It asks whether the work deserves to consume attention, time, money, and future maintenance. It forces the team to clarify the intended change before optimizing the work. What problem should improve? Who should benefit? What risk should go down? What behavior should be different? What would make this work unnecessary?</p><p>Once that direction is clear, efficiency becomes useful. The team can ask how to reduce waste while pursuing a meaningful outcome. Can we slice the work smaller? Can we learn before building the full solution? Can we reduce handoffs? Can we improve tests? Can we simplify the design? Can we remove friction from the path of valuable work?</p><p>Only then does productivity become healthy. At that point, producing more means producing more aligned work through a system that is not burning unnecessary capacity. The team is no longer trying to maximize activity. It is trying to increase the flow of valuable change.</p><p>The wrong order creates three predictable failures:</p><ol><li><p><strong>Productivity first:</strong> the team produces more before knowing whether the work matters.</p></li><li><p><strong>Efficiency first:</strong> the team removes waste from a path that may still point in the wrong direction.</p></li><li><p><strong>Effectiveness first:</strong> the team protects direction before improving the way it moves.</p></li></ol><p>That does not mean productivity and efficiency are secondary in importance. It means they depend on context. Efficiency without effectiveness lacks judgment. Productivity without effectiveness lacks purpose. But effectiveness without productivity and efficiency can remain only a good intention.</p><p>The strongest order is not philosophical. It is practical:</p><ol><li><p><strong>First, ask if the work should exist.</strong></p></li><li><p><strong>Then, ask how to do it with less waste.</strong></p></li><li><p><strong>Finally, ask how to produce more of that valuable work.</strong></p></li></ol><p>Changing the order does not merely change the conversation. It changes what the system learns to reward. If the organization rewards productivity first, people learn to produce visible work. If it rewards efficiency first, people learn to optimize the process. If it rewards effectiveness first, people learn to care about whether reality improved.</p><p>That is why the order matters. The first question frames the system. The second question shapes the cost. The third question scales the result.</p><h2>The first trap: productive but ineffective</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8i4S!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2ec2987-7cae-4052-b89f-a6cc59aec242_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8i4S!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2ec2987-7cae-4052-b89f-a6cc59aec242_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!8i4S!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2ec2987-7cae-4052-b89f-a6cc59aec242_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!8i4S!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2ec2987-7cae-4052-b89f-a6cc59aec242_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!8i4S!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2ec2987-7cae-4052-b89f-a6cc59aec242_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8i4S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2ec2987-7cae-4052-b89f-a6cc59aec242_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c2ec2987-7cae-4052-b89f-a6cc59aec242_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:974959,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2ec2987-7cae-4052-b89f-a6cc59aec242_1024x559.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_!8i4S!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2ec2987-7cae-4052-b89f-a6cc59aec242_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!8i4S!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2ec2987-7cae-4052-b89f-a6cc59aec242_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!8i4S!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2ec2987-7cae-4052-b89f-a6cc59aec242_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!8i4S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2ec2987-7cae-4052-b89f-a6cc59aec242_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The first trap is the most visible in software organizations. A team produces a lot of work, but the product does not get better.</p><p>The roadmap moves. The sprint board clears. Pull requests are merged. Demos happen. Release notes are written. Everyone can point to output. But customers do not change behavior. Support pain remains. Revenue does not move. The onboarding problem survives. The operational problem returns next week. The strategic risk stays untouched.</p><p>This is productive but ineffective.</p><p>It often happens when teams confuse stakeholder demand with validated need. Someone important asks for something. The request enters the backlog. The team breaks it into tickets. The tickets become work. The work becomes output. The output becomes a release. But nobody asks whether the original request was the best way to improve the 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_!YAwa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed11b11-15db-47db-8a83-8f4c50cf10b1_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YAwa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed11b11-15db-47db-8a83-8f4c50cf10b1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!YAwa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed11b11-15db-47db-8a83-8f4c50cf10b1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!YAwa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed11b11-15db-47db-8a83-8f4c50cf10b1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!YAwa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed11b11-15db-47db-8a83-8f4c50cf10b1_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YAwa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed11b11-15db-47db-8a83-8f4c50cf10b1_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7ed11b11-15db-47db-8a83-8f4c50cf10b1_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:953174,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed11b11-15db-47db-8a83-8f4c50cf10b1_1024x559.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_!YAwa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed11b11-15db-47db-8a83-8f4c50cf10b1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!YAwa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed11b11-15db-47db-8a83-8f4c50cf10b1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!YAwa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed11b11-15db-47db-8a83-8f4c50cf10b1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!YAwa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed11b11-15db-47db-8a83-8f4c50cf10b1_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The danger is that this trap feels responsible. Everyone is busy. Everyone is doing their part. Nobody is obviously negligent. The machine is working. The problem is that the machine is pointed at the wrong target.</p><p>In software economics, this creates inventory. Not only unfinished work, but owned work. Code that must be maintained. Features that must be supported. Configuration that must be understood. Edge cases that must be tested. Documentation that must be updated. User expectations that must be honored.</p><p>The team produced work. The system inherited responsibility.</p><h2>The second trap: effective but inefficient</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UheO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9ef1057-33c2-4ff1-a023-be7e620a8262_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UheO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9ef1057-33c2-4ff1-a023-be7e620a8262_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!UheO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9ef1057-33c2-4ff1-a023-be7e620a8262_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!UheO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9ef1057-33c2-4ff1-a023-be7e620a8262_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!UheO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9ef1057-33c2-4ff1-a023-be7e620a8262_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UheO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9ef1057-33c2-4ff1-a023-be7e620a8262_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c9ef1057-33c2-4ff1-a023-be7e620a8262_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:821923,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9ef1057-33c2-4ff1-a023-be7e620a8262_1024x558.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_!UheO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9ef1057-33c2-4ff1-a023-be7e620a8262_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!UheO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9ef1057-33c2-4ff1-a023-be7e620a8262_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!UheO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9ef1057-33c2-4ff1-a023-be7e620a8262_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!UheO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9ef1057-33c2-4ff1-a023-be7e620a8262_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The second trap is more subtle because it produces real value. The team works on the right thing. Customers benefit. The business improves. The product moves in a good direction. But every improvement costs too much.</p><p>This is effective but inefficient.</p><p>You see it when a small feature requires touching seven services, coordinating three teams, manually testing ten flows, waiting days for review, and holding your breath during release. You see it when everyone agrees on the right product decision, but the codebase punishes the team for trying to implement it. You see it when the team can still deliver, but only through stress, memory, heroics, and informal knowledge.</p><p>This trap often hides behind success. Since the outcome is good, the organization underestimates the damage done by the path. The feature worked, so the process is considered acceptable. The customer is happy, so the technical pain is postponed. The release succeeded, so the release anxiety is normalized.</p><p>But the way we produce value shapes the value we can afford to produce next.</p><p>If every useful change leaves behind more coupling, more fragile tests, more operational burden, and more cognitive load, the team is not only delivering. It is borrowing against future capacity. Eventually, the organization stops asking what is valuable and starts asking what is survivable.</p><p>That is a dangerous moment. It means the cost of change has started to dominate product judgment.</p><h2>The third trap: efficient but ineffective</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!W2qV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69aa09d4-1e45-4c48-90f4-18e8077ea5ff_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!W2qV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69aa09d4-1e45-4c48-90f4-18e8077ea5ff_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!W2qV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69aa09d4-1e45-4c48-90f4-18e8077ea5ff_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!W2qV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69aa09d4-1e45-4c48-90f4-18e8077ea5ff_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!W2qV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69aa09d4-1e45-4c48-90f4-18e8077ea5ff_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!W2qV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69aa09d4-1e45-4c48-90f4-18e8077ea5ff_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/69aa09d4-1e45-4c48-90f4-18e8077ea5ff_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1033709,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69aa09d4-1e45-4c48-90f4-18e8077ea5ff_1024x559.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_!W2qV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69aa09d4-1e45-4c48-90f4-18e8077ea5ff_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!W2qV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69aa09d4-1e45-4c48-90f4-18e8077ea5ff_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!W2qV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69aa09d4-1e45-4c48-90f4-18e8077ea5ff_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!W2qV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69aa09d4-1e45-4c48-90f4-18e8077ea5ff_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The third trap is especially attractive in modern software organizations because it looks clean.</p><p>The team has short meetings. The pipeline is fast. The tickets are clear. The dashboards are polished. The ceremonies are optimized. Handoffs are reduced. The delivery process looks smooth. Everything moves.</p><p>But the product still does not improve.</p><p>This is efficient but ineffective.</p><p>Efficiency is not wisdom. A very efficient system can still produce the wrong thing. A perfect factory producing unwanted goods is not a good factory. A software team that delivers the wrong product with low friction is still wasting capacity.</p><p>This trap appears when organizations become better at delivery than discovery. They improve execution without improving judgment. They automate the path from idea to production, but they do not improve the quality of the ideas entering the path.</p><p>This is how teams create polished waste. The work is well-shaped, well-tracked, well-tested, well-deployed, and well-presented. It is just not worth much.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RwBL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb566f05-5cbb-48ba-a2f4-44927230619e_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RwBL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb566f05-5cbb-48ba-a2f4-44927230619e_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!RwBL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb566f05-5cbb-48ba-a2f4-44927230619e_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!RwBL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb566f05-5cbb-48ba-a2f4-44927230619e_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!RwBL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb566f05-5cbb-48ba-a2f4-44927230619e_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RwBL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb566f05-5cbb-48ba-a2f4-44927230619e_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/db566f05-5cbb-48ba-a2f4-44927230619e_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:974401,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb566f05-5cbb-48ba-a2f4-44927230619e_1024x559.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_!RwBL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb566f05-5cbb-48ba-a2f4-44927230619e_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!RwBL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb566f05-5cbb-48ba-a2f4-44927230619e_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!RwBL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb566f05-5cbb-48ba-a2f4-44927230619e_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!RwBL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb566f05-5cbb-48ba-a2f4-44927230619e_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A fast pipeline does not fix a weak product decision. A clean backlog does not prove the work matters. A smooth process does not compensate for a bad bet.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4isu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff164133c-6f37-47c5-ab33-2bdbd819a64c_1747x1347.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4isu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff164133c-6f37-47c5-ab33-2bdbd819a64c_1747x1347.png 424w, https://substackcdn.com/image/fetch/$s_!4isu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff164133c-6f37-47c5-ab33-2bdbd819a64c_1747x1347.png 848w, https://substackcdn.com/image/fetch/$s_!4isu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff164133c-6f37-47c5-ab33-2bdbd819a64c_1747x1347.png 1272w, https://substackcdn.com/image/fetch/$s_!4isu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff164133c-6f37-47c5-ab33-2bdbd819a64c_1747x1347.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4isu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff164133c-6f37-47c5-ab33-2bdbd819a64c_1747x1347.png" width="1456" height="1123" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f164133c-6f37-47c5-ab33-2bdbd819a64c_1747x1347.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1123,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:137135,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff164133c-6f37-47c5-ab33-2bdbd819a64c_1747x1347.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_!4isu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff164133c-6f37-47c5-ab33-2bdbd819a64c_1747x1347.png 424w, https://substackcdn.com/image/fetch/$s_!4isu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff164133c-6f37-47c5-ab33-2bdbd819a64c_1747x1347.png 848w, https://substackcdn.com/image/fetch/$s_!4isu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff164133c-6f37-47c5-ab33-2bdbd819a64c_1747x1347.png 1272w, https://substackcdn.com/image/fetch/$s_!4isu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff164133c-6f37-47c5-ab33-2bdbd819a64c_1747x1347.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>These three traps are useful because they stop the conversation from becoming moral. The question is not whether a team is good or bad. The question is which kind of failure is present, and which question the team has avoided.</p><h2>Examples from software engineering</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uvFB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45c94caa-6a78-4bb7-ab08-e1035d698b00_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uvFB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45c94caa-6a78-4bb7-ab08-e1035d698b00_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uvFB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45c94caa-6a78-4bb7-ab08-e1035d698b00_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uvFB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45c94caa-6a78-4bb7-ab08-e1035d698b00_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uvFB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45c94caa-6a78-4bb7-ab08-e1035d698b00_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uvFB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45c94caa-6a78-4bb7-ab08-e1035d698b00_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/45c94caa-6a78-4bb7-ab08-e1035d698b00_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1009468,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45c94caa-6a78-4bb7-ab08-e1035d698b00_1024x559.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_!uvFB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45c94caa-6a78-4bb7-ab08-e1035d698b00_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uvFB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45c94caa-6a78-4bb7-ab08-e1035d698b00_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uvFB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45c94caa-6a78-4bb7-ab08-e1035d698b00_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uvFB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45c94caa-6a78-4bb7-ab08-e1035d698b00_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A feature can be judged through all three lenses. Suppose a team delivers a new reporting screen. Productivity asks whether the screen was built and released. Effectiveness asks whether users make better decisions because of it. Efficiency asks whether the team delivered it without unnecessary complexity, rework, or future maintenance burden.</p><p>A bug fix works the same way. Productivity asks whether the bug was fixed. Effectiveness asks whether the user pain disappeared or whether the team only patched a symptom. Efficiency asks whether the diagnosis, test coverage, review, and release path were proportional to the risk and impact of the problem.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wFb6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c8ce877-60f6-4a30-ac2f-732190f20ff1_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wFb6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c8ce877-60f6-4a30-ac2f-732190f20ff1_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!wFb6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c8ce877-60f6-4a30-ac2f-732190f20ff1_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!wFb6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c8ce877-60f6-4a30-ac2f-732190f20ff1_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!wFb6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c8ce877-60f6-4a30-ac2f-732190f20ff1_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wFb6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c8ce877-60f6-4a30-ac2f-732190f20ff1_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c8ce877-60f6-4a30-ac2f-732190f20ff1_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1113085,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c8ce877-60f6-4a30-ac2f-732190f20ff1_1024x558.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_!wFb6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c8ce877-60f6-4a30-ac2f-732190f20ff1_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!wFb6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c8ce877-60f6-4a30-ac2f-732190f20ff1_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!wFb6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c8ce877-60f6-4a30-ac2f-732190f20ff1_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!wFb6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c8ce877-60f6-4a30-ac2f-732190f20ff1_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Refactoring is another useful example because it is often misunderstood. A team spends three days improving a messy module. Productivity says engineering work was produced. Effectiveness asks whether the refactoring made an actual upcoming change safer, cheaper, or easier. Efficiency asks whether the improvement was proportional or whether the team expanded the scope beyond the economic need.</p><p>This is where the idea of tidying is helpful. Sometimes the best move before changing behavior is not a grand redesign. It is a small structural improvement that lowers the cost of the next change. Rename something. Move a function. Separate two concerns. Clarify a test. Reduce a little confusion before it charges interest. The point is not to beautify the codebase. The point is to improve the economics of the imminent work.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DGfM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb9d6d6b-fdbc-45bb-b5d4-4fa659113437_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DGfM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb9d6d6b-fdbc-45bb-b5d4-4fa659113437_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!DGfM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb9d6d6b-fdbc-45bb-b5d4-4fa659113437_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!DGfM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb9d6d6b-fdbc-45bb-b5d4-4fa659113437_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!DGfM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb9d6d6b-fdbc-45bb-b5d4-4fa659113437_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DGfM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb9d6d6b-fdbc-45bb-b5d4-4fa659113437_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eb9d6d6b-fdbc-45bb-b5d4-4fa659113437_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1133169,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb9d6d6b-fdbc-45bb-b5d4-4fa659113437_1024x558.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_!DGfM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb9d6d6b-fdbc-45bb-b5d4-4fa659113437_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!DGfM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb9d6d6b-fdbc-45bb-b5d4-4fa659113437_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!DGfM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb9d6d6b-fdbc-45bb-b5d4-4fa659113437_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!DGfM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb9d6d6b-fdbc-45bb-b5d4-4fa659113437_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Automated tests also need the three lenses. A team adds 200 tests. Productive? Yes, tests were produced. Effective? Only if they protect meaningful behavior and catch regressions that matter. Efficient? Only if they give useful feedback without excessive runtime, brittleness, maintenance cost, or diagnostic noise.</p><p><em>Test-Driven Development</em> (<em>TDD</em>) should be understood the same way. It is not productive because many tests exist. It is effective when the tests help clarify behavior, discover design, protect intent, and support safe change. It is efficient when the feedback loop stays small enough to help the team learn quickly without turning the process into ceremony.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!maIh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84aee528-8718-4999-9532-d8970581d34f_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!maIh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84aee528-8718-4999-9532-d8970581d34f_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!maIh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84aee528-8718-4999-9532-d8970581d34f_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!maIh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84aee528-8718-4999-9532-d8970581d34f_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!maIh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84aee528-8718-4999-9532-d8970581d34f_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!maIh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84aee528-8718-4999-9532-d8970581d34f_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/84aee528-8718-4999-9532-d8970581d34f_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1072417,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84aee528-8718-4999-9532-d8970581d34f_1024x559.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_!maIh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84aee528-8718-4999-9532-d8970581d34f_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!maIh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84aee528-8718-4999-9532-d8970581d34f_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!maIh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84aee528-8718-4999-9532-d8970581d34f_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!maIh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84aee528-8718-4999-9532-d8970581d34f_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Continuous Integration</em> (<em>CI</em>) and <em>Continuous Delivery</em> (<em>CD</em>) also show the distinction clearly. Building a faster pipeline is productive engineering output. It is effective if it improves integration, release safety, and feedback. It is efficient if it reduces waiting, manual coordination, rework, and release anxiety without creating unnecessary operational machinery.</p><p>The same activity can be good or bad depending on the lens. That is why teams need sharper language.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ax8v!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202aa52a-6bc9-40cc-9078-2e777ffaaa1e_2036x764.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ax8v!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202aa52a-6bc9-40cc-9078-2e777ffaaa1e_2036x764.png 424w, https://substackcdn.com/image/fetch/$s_!Ax8v!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202aa52a-6bc9-40cc-9078-2e777ffaaa1e_2036x764.png 848w, https://substackcdn.com/image/fetch/$s_!Ax8v!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202aa52a-6bc9-40cc-9078-2e777ffaaa1e_2036x764.png 1272w, https://substackcdn.com/image/fetch/$s_!Ax8v!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202aa52a-6bc9-40cc-9078-2e777ffaaa1e_2036x764.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ax8v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202aa52a-6bc9-40cc-9078-2e777ffaaa1e_2036x764.png" width="1456" height="546" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/202aa52a-6bc9-40cc-9078-2e777ffaaa1e_2036x764.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:546,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:95931,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202aa52a-6bc9-40cc-9078-2e777ffaaa1e_2036x764.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_!Ax8v!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202aa52a-6bc9-40cc-9078-2e777ffaaa1e_2036x764.png 424w, https://substackcdn.com/image/fetch/$s_!Ax8v!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202aa52a-6bc9-40cc-9078-2e777ffaaa1e_2036x764.png 848w, https://substackcdn.com/image/fetch/$s_!Ax8v!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202aa52a-6bc9-40cc-9078-2e777ffaaa1e_2036x764.png 1272w, https://substackcdn.com/image/fetch/$s_!Ax8v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202aa52a-6bc9-40cc-9078-2e777ffaaa1e_2036x764.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The same work should not be judged with one word. A refactoring can be technically impressive and economically weak. A test suite can be large and ineffective. A pipeline can be fast and still irrelevant if it accelerates the wrong decisions.</p><h2>How teams should use these words</h2><p>Teams should use productivity, effectiveness, and efficiency as separate conversation tools. Each word should trigger a different discussion.</p><p>When discussing productivity, ask what is being produced. Be precise. Do not say &#8220;the team is productive&#8221; as a general compliment. Say the team is producing more releasable changes, more validated learning, more operational improvements, more useful tests, or more customer-facing value. The object matters.</p><p>When discussing effectiveness, ask why the work matters. What problem should change? Who benefits? What behavior should be different? What risk should go down? What decision will this help us make? What would convince us that the work did not matter?</p><p>When discussing efficiency, ask where waste appears. Where do we wait? Where do we rework? Where do we lose context? Where do we hand work across boundaries? Where does cognitive load slow us down? Where does the codebase make valuable change unnecessarily expensive?</p><p>Also ask one more uncomfortable question: are we calling this efficient only because everyone looks busy? If the answer is yes, the team is probably measuring occupation, not efficiency. The system exists to move valuable work from need to outcome. If it is full but not moving, it is not healthy.</p><p>These questions should not be mixed too early. If someone asks whether a team is productive, do not answer with a velocity number before asking productive at what. If someone asks whether a feature was successful, do not answer only with delivery date. If someone asks whether a process is efficient, do not answer only with speed if the output is low-value.</p><p>A good team conversation might sound like this:</p><ol><li><p><strong>Effectiveness:</strong> &#8220;This feature shipped, but did it reduce the customer problem we expected to reduce?&#8221;</p></li><li><p><strong>Efficiency:</strong> &#8220;The outcome was good, but why did such a small change take so much coordination?&#8221;</p></li><li><p><strong>Productivity:</strong> &#8220;Now that we know this type of work matters, how can we increase the flow of similar valuable changes?&#8221;<br>That order changes the culture. It moves the team away from activity worship and toward economic judgment.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!W-WE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b5473f-d5f8-41bf-acce-296d237b5b4c_4899x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!W-WE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b5473f-d5f8-41bf-acce-296d237b5b4c_4899x188.png 424w, https://substackcdn.com/image/fetch/$s_!W-WE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b5473f-d5f8-41bf-acce-296d237b5b4c_4899x188.png 848w, https://substackcdn.com/image/fetch/$s_!W-WE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b5473f-d5f8-41bf-acce-296d237b5b4c_4899x188.png 1272w, https://substackcdn.com/image/fetch/$s_!W-WE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b5473f-d5f8-41bf-acce-296d237b5b4c_4899x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!W-WE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b5473f-d5f8-41bf-acce-296d237b5b4c_4899x188.png" width="1456" height="56" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d1b5473f-d5f8-41bf-acce-296d237b5b4c_4899x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:56,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:66531,&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://emmanuelvalverderamos.substack.com/i/198517600?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b5473f-d5f8-41bf-acce-296d237b5b4c_4899x188.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_!W-WE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b5473f-d5f8-41bf-acce-296d237b5b4c_4899x188.png 424w, https://substackcdn.com/image/fetch/$s_!W-WE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b5473f-d5f8-41bf-acce-296d237b5b4c_4899x188.png 848w, https://substackcdn.com/image/fetch/$s_!W-WE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b5473f-d5f8-41bf-acce-296d237b5b4c_4899x188.png 1272w, https://substackcdn.com/image/fetch/$s_!W-WE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b5473f-d5f8-41bf-acce-296d237b5b4c_4899x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>This is not a semantic game. It is a practical way to avoid bad management conversations. The team stops using productivity as a moral label and starts using it as one lens among several.</p><h2>Conclusions</h2><p>Productivity, effectiveness, and efficiency are simple words, but software makes them easy to confuse.</p><p>Productivity means work was produced. Effectiveness means the work achieved the right result. Efficiency means the result was produced with a responsible use of resources. The three ideas are connected, but they are not interchangeable.</p><p>This matters because software output does not vanish after delivery. It becomes part of the product&#8217;s economic reality. Code must be maintained. Features must be supported. Tests must be trusted. Pipelines must be operated. Decisions must be explained. Complexity must be carried.</p><p>That is why producing more is not automatically better. More of the wrong work is inventory. More valuable work through a painful system is fragility. More efficiency in the wrong direction is polished waste.</p><p>The best software teams do not use productivity as a moral word. They do not celebrate output before asking whether it mattered. They do not confuse motion with progress, flow with value, or busyness with efficiency.</p><p>They separate the questions.</p><p>Are we doing the right work?<br>Are we doing it without unnecessary waste?<br>Are we producing enough of the work that truly matters?</p><p>That is the practical difference. Productivity tells us that the team moved. Effectiveness tells us whether it moved in the right direction. Efficiency tells us whether the movement consumed more than it should.</p><p>Strong teams need all three. But they must never pretend they are the same.</p><h2>References</h2><ul><li><p>Cambridge University Press. <em>Productivity</em>. <a href="https://dictionary.cambridge.org/dictionary/english/productivity">https://dictionary.cambridge.org/dictionary/english/productivity</a></p></li><li><p>Cambridge University Press. <em>Effectiveness</em>. <a href="https://dictionary.cambridge.org/dictionary/english/effectiveness">https://dictionary.cambridge.org/dictionary/english/effectiveness</a></p></li><li><p>Cambridge University Press. <em>Efficiency</em>. <a href="https://dictionary.cambridge.org/dictionary/english/efficiency">https://dictionary.cambridge.org/dictionary/english/efficiency</a></p></li><li><p>Valverde Ramos, Emmanuel. <em>Output, Outcome, and Throughput: What Product Teams are really measuring when they say they deliver value</em>. <a href="https://emmanuelvalverderamos.substack.com/p/output-outcome-and-throughput-what">https://emmanuelvalverderamos.substack.com/p/output-outcome-and-throughput-what</a></p></li><li><p>Valverde Ramos, Emmanuel. <em>The Resource Utilization Trap: Why Keeping Everyone Busy Can Destroy Real Productivity</em>. <a href="https://emmanuelvalverderamos.substack.com/p/the-resource-utilization-trap-why">https://emmanuelvalverderamos.substack.com/p/the-resource-utilization-trap-why</a></p></li><li><p>Forsgren, Nicole; Storey, Margaret-Anne; Maddila, Chandra; Zimmermann, Thomas; Houck, Brian; Butler, Jenna. <em>The SPACE of Developer Productivity</em>. <a href="https://queue.acm.org/detail.cfm?id=3454124">https://queue.acm.org/detail.cfm?id=3454124</a></p></li><li><p>Noda, Abi; Storey, Margaret-Anne; Forsgren, Nicole; Greiler, Michaela. <em>DevEx: What Actually Drives Productivity</em>. <a href="https://queue.acm.org/detail.cfm?id=3595878">https://queue.acm.org/detail.cfm?id=3595878</a></p></li><li><p>Fowler, Martin. <em>Outcome Over Output</em>. <a href="https://martinfowler.com/bliki/OutcomeOverOutput.html">https://martinfowler.com/bliki/OutcomeOverOutput.html</a></p></li><li><p>Beck, Kent. <em>Test-Driven Development: By Example</em>. Addison-Wesley, 2003</p></li><li><p>Beck, Kent. <em>Tidy First?</em>. O&#8217;Reilly Media, 2023</p></li><li><p>Reinertsen, Donald G. <em>The Principles of Product Development Flow</em>. Celeritas Publishing, 2009</p></li><li><p>Poppendieck, Mary; Poppendieck, Tom. <em>Lean Software Development: An Agile Toolkit</em>. Addison-Wesley, 2003</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Kata 2: Library loan]]></title><description><![CDATA[The second of three katas to practice Object design]]></description><link>https://emmanuelvalverderamos.substack.com/p/kata-2-library-loan</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/kata-2-library-loan</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 11 Aug 2026 06:01:01 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!gXvm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05de8386-413d-4e50-9713-2ff059e2f56b_914x972.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Training intent</h2><p>Practise designing operations as contracts: what must be true before an operation, what happens when the operation is accepted, and what must remain true when the operation is rejected.</p><p>This kata is not about records, tables, CRUD screens, or status fields. It is about behaviour, conditions, aftereffects, and safe collaboration.</p><h2>Business context</h2><p>A small library lends books to members.</p><p>Members can borrow available books up to a fixed limit. A book already borrowed by one member cannot be borrowed by another. A borrowed book may be reserved by another member. When a reserved book is returned, the waiting member gets priority.</p><h2>Problem to solve</h2><p>Build the behaviour needed to borrow, return, and reserve books safely.</p><p>A member should be able to:</p><ul><li><p>Borrow an available book</p></li><li><p>See their borrowed books</p></li><li><p>Return a book they borrowed</p></li><li><p>Reserve a book borrowed by another member</p></li><li><p>Borrow a returned book that was reserved for them</p></li></ul><p>The library must reject invalid operations and preserve the previous valid state.</p><h2>Vocabulary</h2><p>Use this vocabulary to describe the business problem. It is not a proposed implementation model.</p><ul><li><p>Library</p></li><li><p>Book</p></li><li><p>Member</p></li><li><p>Borrowed book</p></li><li><p>Available book</p></li><li><p>Waiting member</p></li><li><p>Reservation</p></li><li><p>Loan limit</p></li><li><p>Borrowing request</p></li><li><p>Return request</p></li><li><p>Reservation request</p></li><li><p>Rejected request</p></li></ul><h2>Fixed rules</h2><ul><li><p>A member may borrow at most 3 books.</p></li><li><p>A book can be borrowed by one member at a time.</p></li><li><p>A member cannot reserve a book they already borrowed.</p></li><li><p>An available book cannot be reserved.</p></li><li><p>A returned reserved book is available only to the waiting member.</p></li><li><p>A rejected operation must not change the previous valid state.</p></li></ul><h2>Business lifecycle</h2><p>This diagram describes the business lifecycle of a book in this kata. It is not an implementation model.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gXvm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05de8386-413d-4e50-9713-2ff059e2f56b_914x972.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gXvm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05de8386-413d-4e50-9713-2ff059e2f56b_914x972.png 424w, https://substackcdn.com/image/fetch/$s_!gXvm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05de8386-413d-4e50-9713-2ff059e2f56b_914x972.png 848w, https://substackcdn.com/image/fetch/$s_!gXvm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05de8386-413d-4e50-9713-2ff059e2f56b_914x972.png 1272w, https://substackcdn.com/image/fetch/$s_!gXvm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05de8386-413d-4e50-9713-2ff059e2f56b_914x972.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gXvm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05de8386-413d-4e50-9713-2ff059e2f56b_914x972.png" width="914" height="972" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/05de8386-413d-4e50-9713-2ff059e2f56b_914x972.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:972,&quot;width&quot;:914,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:85340,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05de8386-413d-4e50-9713-2ff059e2f56b_914x972.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_!gXvm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05de8386-413d-4e50-9713-2ff059e2f56b_914x972.png 424w, https://substackcdn.com/image/fetch/$s_!gXvm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05de8386-413d-4e50-9713-2ff059e2f56b_914x972.png 848w, https://substackcdn.com/image/fetch/$s_!gXvm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05de8386-413d-4e50-9713-2ff059e2f56b_914x972.png 1272w, https://substackcdn.com/image/fetch/$s_!gXvm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05de8386-413d-4e50-9713-2ff059e2f56b_914x972.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>Scenario map</h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!y6E4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df35979-53f6-4969-ace1-33291fbbc781_3697x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!y6E4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df35979-53f6-4969-ace1-33291fbbc781_3697x140.png 424w, https://substackcdn.com/image/fetch/$s_!y6E4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df35979-53f6-4969-ace1-33291fbbc781_3697x140.png 848w, https://substackcdn.com/image/fetch/$s_!y6E4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df35979-53f6-4969-ace1-33291fbbc781_3697x140.png 1272w, https://substackcdn.com/image/fetch/$s_!y6E4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df35979-53f6-4969-ace1-33291fbbc781_3697x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!y6E4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df35979-53f6-4969-ace1-33291fbbc781_3697x140.png" width="1456" height="55" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1df35979-53f6-4969-ace1-33291fbbc781_3697x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:55,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:37995,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df35979-53f6-4969-ace1-33291fbbc781_3697x140.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_!y6E4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df35979-53f6-4969-ace1-33291fbbc781_3697x140.png 424w, https://substackcdn.com/image/fetch/$s_!y6E4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df35979-53f6-4969-ace1-33291fbbc781_3697x140.png 848w, https://substackcdn.com/image/fetch/$s_!y6E4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df35979-53f6-4969-ace1-33291fbbc781_3697x140.png 1272w, https://substackcdn.com/image/fetch/$s_!y6E4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df35979-53f6-4969-ace1-33291fbbc781_3697x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Business contracts</h2><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/DLKki/2/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/037b0569-787e-4a0c-a2ce-736aa03455b4_1220x706.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4691f9f9-c154-474c-accb-ea8fa5a2f32e_1220x776.png&quot;,&quot;height&quot;:391,&quot;title&quot;:&quot;Business contracts&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/DLKki/2/" width="730" height="391" frameborder="0" scrolling="no" loading="lazy"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><h2>Participant checklist</h2><p>Before moving to the next scenario:</p><ul><li><p>Is the behaviour covered by a failing test first?</p></li><li><p>Is the expected outcome observable from outside the implementation?</p></li><li><p>Are previous scenarios still passing?</p></li><li><p>Did a rejected operation preserve the previous state?</p></li><li><p>Does the test describe behaviour rather than internal data?</p></li><li><p>Is there duplication or awkward naming worth refactoring now?</p></li></ul><div class="callout-block" data-callout="true"><p><strong>&#9888;&#65039; This kata has counterexample that you do not have to follow, they are meant to force you, to think about the sad path</strong></p></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Rule 1: The library starts with no borrowed books</h2><h3>What this rule means</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1CXz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feafbdc84-cff2-4b4e-8e13-bfabc52265d4_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1CXz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feafbdc84-cff2-4b4e-8e13-bfabc52265d4_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!1CXz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feafbdc84-cff2-4b4e-8e13-bfabc52265d4_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!1CXz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feafbdc84-cff2-4b4e-8e13-bfabc52265d4_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!1CXz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feafbdc84-cff2-4b4e-8e13-bfabc52265d4_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1CXz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feafbdc84-cff2-4b4e-8e13-bfabc52265d4_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eafbdc84-cff2-4b4e-8e13-bfabc52265d4_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:506096,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feafbdc84-cff2-4b4e-8e13-bfabc52265d4_1024x559.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_!1CXz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feafbdc84-cff2-4b4e-8e13-bfabc52265d4_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!1CXz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feafbdc84-cff2-4b4e-8e13-bfabc52265d4_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!1CXz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feafbdc84-cff2-4b4e-8e13-bfabc52265d4_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!1CXz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feafbdc84-cff2-4b4e-8e13-bfabc52265d4_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A member who has not borrowed anything should have no borrowed books.</p><p>This is the smallest observable behaviour in the kata. It gives the system a clear starting point before any borrowing, returning, or reservation rules appear.</p><h3>Why this rule matters</h3><p>Before the library can track changes, it must be clear what the initial state looks like.</p><p>A new member should not appear to have borrowed books by default. If the system cannot represent &#8220;nothing has happened yet&#8221;, every later change becomes harder to trust.</p><h3>Example</h3><p>Alice is a library member. She has not borrowed any books.</p><p>When the library checks Alice&#8217;s borrowed books, none are shown.</p><h3>Counterexample</h3><p>Alice has not borrowed anything, but the library shows one or more borrowed books.</p><p>That would be wrong because the system would be reporting loans that never happened.</p><h3>What this rule adds to the kata</h3><p>This rule anchors the rest of the kata.</p><p>Every later scenario either changes this observable state or protects it from invalid operations.</p><h3>Rule diagram</h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ypfP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ffeab6b-2fe2-468b-b02a-37bc7adfe6a0_1792x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ypfP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ffeab6b-2fe2-468b-b02a-37bc7adfe6a0_1792x188.png 424w, https://substackcdn.com/image/fetch/$s_!ypfP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ffeab6b-2fe2-468b-b02a-37bc7adfe6a0_1792x188.png 848w, https://substackcdn.com/image/fetch/$s_!ypfP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ffeab6b-2fe2-468b-b02a-37bc7adfe6a0_1792x188.png 1272w, https://substackcdn.com/image/fetch/$s_!ypfP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ffeab6b-2fe2-468b-b02a-37bc7adfe6a0_1792x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ypfP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ffeab6b-2fe2-468b-b02a-37bc7adfe6a0_1792x188.png" width="1456" height="153" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4ffeab6b-2fe2-468b-b02a-37bc7adfe6a0_1792x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:153,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28945,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ffeab6b-2fe2-468b-b02a-37bc7adfe6a0_1792x188.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_!ypfP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ffeab6b-2fe2-468b-b02a-37bc7adfe6a0_1792x188.png 424w, https://substackcdn.com/image/fetch/$s_!ypfP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ffeab6b-2fe2-468b-b02a-37bc7adfe6a0_1792x188.png 848w, https://substackcdn.com/image/fetch/$s_!ypfP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ffeab6b-2fe2-468b-b02a-37bc7adfe6a0_1792x188.png 1272w, https://substackcdn.com/image/fetch/$s_!ypfP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ffeab6b-2fe2-468b-b02a-37bc7adfe6a0_1792x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 1.1: Show no borrowed books for a new member</h3><h4>Business example</h4><p>Alice is a member, but she has not borrowed anything yet.</p><h4>Gherkin</h4><pre><code><code>Scenario: Show no borrowed books for a new member
  Given Alice has not borrowed any books
  When the library checks Alice's borrowed books
  Then no borrowed books are shown</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YvG5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F159ba2aa-e7ae-4671-8556-3e7f56efb053_1782x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YvG5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F159ba2aa-e7ae-4671-8556-3e7f56efb053_1782x188.png 424w, https://substackcdn.com/image/fetch/$s_!YvG5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F159ba2aa-e7ae-4671-8556-3e7f56efb053_1782x188.png 848w, https://substackcdn.com/image/fetch/$s_!YvG5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F159ba2aa-e7ae-4671-8556-3e7f56efb053_1782x188.png 1272w, https://substackcdn.com/image/fetch/$s_!YvG5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F159ba2aa-e7ae-4671-8556-3e7f56efb053_1782x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YvG5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F159ba2aa-e7ae-4671-8556-3e7f56efb053_1782x188.png" width="1456" height="154" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/159ba2aa-e7ae-4671-8556-3e7f56efb053_1782x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:154,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31022,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F159ba2aa-e7ae-4671-8556-3e7f56efb053_1782x188.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_!YvG5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F159ba2aa-e7ae-4671-8556-3e7f56efb053_1782x188.png 424w, https://substackcdn.com/image/fetch/$s_!YvG5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F159ba2aa-e7ae-4671-8556-3e7f56efb053_1782x188.png 848w, https://substackcdn.com/image/fetch/$s_!YvG5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F159ba2aa-e7ae-4671-8556-3e7f56efb053_1782x188.png 1272w, https://substackcdn.com/image/fetch/$s_!YvG5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F159ba2aa-e7ae-4671-8556-3e7f56efb053_1782x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Rule 2: Available books can be borrowed</h2><h3>What this rule means</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZAO5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fc5087e-4610-4bbe-ab45-542451522b92_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZAO5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fc5087e-4610-4bbe-ab45-542451522b92_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ZAO5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fc5087e-4610-4bbe-ab45-542451522b92_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ZAO5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fc5087e-4610-4bbe-ab45-542451522b92_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ZAO5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fc5087e-4610-4bbe-ab45-542451522b92_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZAO5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fc5087e-4610-4bbe-ab45-542451522b92_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9fc5087e-4610-4bbe-ab45-542451522b92_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:591053,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fc5087e-4610-4bbe-ab45-542451522b92_1024x559.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_!ZAO5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fc5087e-4610-4bbe-ab45-542451522b92_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ZAO5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fc5087e-4610-4bbe-ab45-542451522b92_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ZAO5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fc5087e-4610-4bbe-ab45-542451522b92_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ZAO5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fc5087e-4610-4bbe-ab45-542451522b92_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A member can borrow a book when that book is available.</p><p>After the borrowing succeeds, the book is no longer generally available, and the library can show that the member borrowed it.</p><p>This is the first operation with an aftereffect: the world changes after the operation.</p><h3>Why this rule matters</h3><p>Borrowing is the central behaviour of the library.</p><p>If the system cannot express what changes when a book is borrowed, later rules such as loan limits, returns, and reservations have no foundation.</p><p>The important part is not only that the operation succeeds. The important part is that the result of the operation can be observed afterwards.</p><h3>Example</h3><p>&#8220;Domain-Driven Design&#8221; is available.</p><p>Alice borrows it.</p><p>The book is now borrowed by Alice and appears among Alice&#8217;s borrowed books.</p><h3>Counterexample</h3><p>&#8220;Domain-Driven Design&#8221; is available.</p><p>Alice borrows it.</p><p>The system still reports the book as generally available.</p><p>That would be wrong because another member could now borrow the same book.</p><h3>What this rule adds to the kata</h3><p>This rule introduces the first accepted state transition.</p><p>The kata stops being only a query and becomes behaviour that changes what the library knows.</p><h3>Rule diagram</h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tPjm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F411707df-c10c-4f58-b796-f8834991f3b9_2167x444.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tPjm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F411707df-c10c-4f58-b796-f8834991f3b9_2167x444.png 424w, https://substackcdn.com/image/fetch/$s_!tPjm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F411707df-c10c-4f58-b796-f8834991f3b9_2167x444.png 848w, https://substackcdn.com/image/fetch/$s_!tPjm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F411707df-c10c-4f58-b796-f8834991f3b9_2167x444.png 1272w, https://substackcdn.com/image/fetch/$s_!tPjm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F411707df-c10c-4f58-b796-f8834991f3b9_2167x444.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tPjm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F411707df-c10c-4f58-b796-f8834991f3b9_2167x444.png" width="1456" height="298" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/411707df-c10c-4f58-b796-f8834991f3b9_2167x444.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:298,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:58543,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F411707df-c10c-4f58-b796-f8834991f3b9_2167x444.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_!tPjm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F411707df-c10c-4f58-b796-f8834991f3b9_2167x444.png 424w, https://substackcdn.com/image/fetch/$s_!tPjm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F411707df-c10c-4f58-b796-f8834991f3b9_2167x444.png 848w, https://substackcdn.com/image/fetch/$s_!tPjm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F411707df-c10c-4f58-b796-f8834991f3b9_2167x444.png 1272w, https://substackcdn.com/image/fetch/$s_!tPjm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F411707df-c10c-4f58-b796-f8834991f3b9_2167x444.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 2.1: Borrow one available book</h3><h4>Business example</h4><p>Alice borrows a book that nobody else has borrowed.</p><h4>Gherkin</h4><pre><code><code>Scenario: Borrow one available book
  Given "Domain-Driven Design" is available
  When Alice borrows "Domain-Driven Design"
  Then "Domain-Driven Design" is borrowed by Alice</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wm5W!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd63464-4f77-452f-861b-4f62b71311a0_1471x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wm5W!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd63464-4f77-452f-861b-4f62b71311a0_1471x140.png 424w, https://substackcdn.com/image/fetch/$s_!wm5W!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd63464-4f77-452f-861b-4f62b71311a0_1471x140.png 848w, https://substackcdn.com/image/fetch/$s_!wm5W!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd63464-4f77-452f-861b-4f62b71311a0_1471x140.png 1272w, https://substackcdn.com/image/fetch/$s_!wm5W!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd63464-4f77-452f-861b-4f62b71311a0_1471x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wm5W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd63464-4f77-452f-861b-4f62b71311a0_1471x140.png" width="1456" height="139" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6fd63464-4f77-452f-861b-4f62b71311a0_1471x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:139,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19032,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd63464-4f77-452f-861b-4f62b71311a0_1471x140.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_!wm5W!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd63464-4f77-452f-861b-4f62b71311a0_1471x140.png 424w, https://substackcdn.com/image/fetch/$s_!wm5W!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd63464-4f77-452f-861b-4f62b71311a0_1471x140.png 848w, https://substackcdn.com/image/fetch/$s_!wm5W!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd63464-4f77-452f-861b-4f62b71311a0_1471x140.png 1272w, https://substackcdn.com/image/fetch/$s_!wm5W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd63464-4f77-452f-861b-4f62b71311a0_1471x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 2.2: Make a borrowed book unavailable</h3><h4>Business example</h4><p>Once Alice borrows a book, it cannot be treated as generally available.</p><h4>Gherkin</h4><pre><code><code>Scenario: Make a borrowed book unavailable
  Given "Domain-Driven Design" is available
  When Alice borrows "Domain-Driven Design"
  Then "Domain-Driven Design" is not available</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MvQh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dbab5ad-8f39-4e29-81cf-14b5ff1e9206_1399x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MvQh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dbab5ad-8f39-4e29-81cf-14b5ff1e9206_1399x140.png 424w, https://substackcdn.com/image/fetch/$s_!MvQh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dbab5ad-8f39-4e29-81cf-14b5ff1e9206_1399x140.png 848w, https://substackcdn.com/image/fetch/$s_!MvQh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dbab5ad-8f39-4e29-81cf-14b5ff1e9206_1399x140.png 1272w, https://substackcdn.com/image/fetch/$s_!MvQh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dbab5ad-8f39-4e29-81cf-14b5ff1e9206_1399x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MvQh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dbab5ad-8f39-4e29-81cf-14b5ff1e9206_1399x140.png" width="1399" height="140" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1dbab5ad-8f39-4e29-81cf-14b5ff1e9206_1399x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:140,&quot;width&quot;:1399,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16949,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dbab5ad-8f39-4e29-81cf-14b5ff1e9206_1399x140.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_!MvQh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dbab5ad-8f39-4e29-81cf-14b5ff1e9206_1399x140.png 424w, https://substackcdn.com/image/fetch/$s_!MvQh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dbab5ad-8f39-4e29-81cf-14b5ff1e9206_1399x140.png 848w, https://substackcdn.com/image/fetch/$s_!MvQh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dbab5ad-8f39-4e29-81cf-14b5ff1e9206_1399x140.png 1272w, https://substackcdn.com/image/fetch/$s_!MvQh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dbab5ad-8f39-4e29-81cf-14b5ff1e9206_1399x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 2.3: Show one borrowed book for a member</h3><h4>Business example</h4><p>The library can show which book Alice borrowed.</p><h4>Gherkin</h4><pre><code><code>Scenario: Show one borrowed book for a member
  Given Alice borrowed "Domain-Driven Design"
  When the library checks Alice's borrowed books
  Then "Domain-Driven Design" is shown for Alice</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8lmx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45a1c2d6-b5d6-4a56-9261-f50684afebf0_1748x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8lmx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45a1c2d6-b5d6-4a56-9261-f50684afebf0_1748x188.png 424w, https://substackcdn.com/image/fetch/$s_!8lmx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45a1c2d6-b5d6-4a56-9261-f50684afebf0_1748x188.png 848w, https://substackcdn.com/image/fetch/$s_!8lmx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45a1c2d6-b5d6-4a56-9261-f50684afebf0_1748x188.png 1272w, https://substackcdn.com/image/fetch/$s_!8lmx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45a1c2d6-b5d6-4a56-9261-f50684afebf0_1748x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8lmx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45a1c2d6-b5d6-4a56-9261-f50684afebf0_1748x188.png" width="1456" height="157" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/45a1c2d6-b5d6-4a56-9261-f50684afebf0_1748x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:157,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28925,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45a1c2d6-b5d6-4a56-9261-f50684afebf0_1748x188.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_!8lmx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45a1c2d6-b5d6-4a56-9261-f50684afebf0_1748x188.png 424w, https://substackcdn.com/image/fetch/$s_!8lmx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45a1c2d6-b5d6-4a56-9261-f50684afebf0_1748x188.png 848w, https://substackcdn.com/image/fetch/$s_!8lmx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45a1c2d6-b5d6-4a56-9261-f50684afebf0_1748x188.png 1272w, https://substackcdn.com/image/fetch/$s_!8lmx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45a1c2d6-b5d6-4a56-9261-f50684afebf0_1748x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 2.4: Show several borrowed books for a member</h3><h4>Business example</h4><p>A member may borrow more than one book.</p><h4>Gherkin</h4><pre><code><code>Scenario: Show several borrowed books for a member
  Given Alice borrowed 3 books
  When the library checks Alice's borrowed books
  Then the library shows 3 borrowed books for Alice</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!v3Gx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F050571a1-ee01-4043-985d-e8ee176c64d5_1786x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!v3Gx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F050571a1-ee01-4043-985d-e8ee176c64d5_1786x188.png 424w, https://substackcdn.com/image/fetch/$s_!v3Gx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F050571a1-ee01-4043-985d-e8ee176c64d5_1786x188.png 848w, https://substackcdn.com/image/fetch/$s_!v3Gx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F050571a1-ee01-4043-985d-e8ee176c64d5_1786x188.png 1272w, https://substackcdn.com/image/fetch/$s_!v3Gx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F050571a1-ee01-4043-985d-e8ee176c64d5_1786x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!v3Gx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F050571a1-ee01-4043-985d-e8ee176c64d5_1786x188.png" width="1456" height="153" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/050571a1-ee01-4043-985d-e8ee176c64d5_1786x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:153,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:32400,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F050571a1-ee01-4043-985d-e8ee176c64d5_1786x188.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_!v3Gx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F050571a1-ee01-4043-985d-e8ee176c64d5_1786x188.png 424w, https://substackcdn.com/image/fetch/$s_!v3Gx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F050571a1-ee01-4043-985d-e8ee176c64d5_1786x188.png 848w, https://substackcdn.com/image/fetch/$s_!v3Gx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F050571a1-ee01-4043-985d-e8ee176c64d5_1786x188.png 1272w, https://substackcdn.com/image/fetch/$s_!v3Gx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F050571a1-ee01-4043-985d-e8ee176c64d5_1786x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Rule 3: Borrowing has limits</h2><h3>What this rule means</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ur6o!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95cc67e9-1d60-4143-9c73-1ff691d81f62_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ur6o!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95cc67e9-1d60-4143-9c73-1ff691d81f62_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!ur6o!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95cc67e9-1d60-4143-9c73-1ff691d81f62_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!ur6o!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95cc67e9-1d60-4143-9c73-1ff691d81f62_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!ur6o!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95cc67e9-1d60-4143-9c73-1ff691d81f62_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ur6o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95cc67e9-1d60-4143-9c73-1ff691d81f62_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/95cc67e9-1d60-4143-9c73-1ff691d81f62_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:837678,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95cc67e9-1d60-4143-9c73-1ff691d81f62_1024x558.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_!ur6o!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95cc67e9-1d60-4143-9c73-1ff691d81f62_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!ur6o!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95cc67e9-1d60-4143-9c73-1ff691d81f62_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!ur6o!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95cc67e9-1d60-4143-9c73-1ff691d81f62_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!ur6o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95cc67e9-1d60-4143-9c73-1ff691d81f62_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A member can borrow up to 3 books.</p><p>If they already have 3 borrowed books, a new borrowing request must be rejected.</p><p>The important part is not only rejection. The requested book must remain available because the borrowing did not happen.</p><h3>Why this rule matters</h3><p>The library has a policy that controls how many books a member can hold at the same time.</p><p>The system must protect that policy while keeping the rest of the library state consistent.</p><p>This is the first rule where borrowing depends not only on the book, but also on the member&#8217;s current situation.</p><h3>Example</h3><p>Alice has already borrowed 2 books.</p><p>She borrows another available book.</p><p>Alice now has 3 borrowed books.</p><h3>Counterexample</h3><p>Alice has already borrowed 3 books.</p><p>She tries to borrow &#8220;Refactoring&#8221;.</p><p>The request is rejected and &#8220;Refactoring&#8221; remains available.</p><h3>What this rule adds to the kata</h3><p>The kata now has a business policy.</p><p>Borrowing is no longer valid just because the book is available. The member&#8217;s situation also matters.</p><h3>Rule diagram</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KmiZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbb44985-a767-4f5e-bfa1-f8e77c20939f_2300x588.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KmiZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbb44985-a767-4f5e-bfa1-f8e77c20939f_2300x588.png 424w, https://substackcdn.com/image/fetch/$s_!KmiZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbb44985-a767-4f5e-bfa1-f8e77c20939f_2300x588.png 848w, https://substackcdn.com/image/fetch/$s_!KmiZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbb44985-a767-4f5e-bfa1-f8e77c20939f_2300x588.png 1272w, https://substackcdn.com/image/fetch/$s_!KmiZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbb44985-a767-4f5e-bfa1-f8e77c20939f_2300x588.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KmiZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbb44985-a767-4f5e-bfa1-f8e77c20939f_2300x588.png" width="1456" height="372" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dbb44985-a767-4f5e-bfa1-f8e77c20939f_2300x588.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:372,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:70705,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbb44985-a767-4f5e-bfa1-f8e77c20939f_2300x588.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_!KmiZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbb44985-a767-4f5e-bfa1-f8e77c20939f_2300x588.png 424w, https://substackcdn.com/image/fetch/$s_!KmiZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbb44985-a767-4f5e-bfa1-f8e77c20939f_2300x588.png 848w, https://substackcdn.com/image/fetch/$s_!KmiZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbb44985-a767-4f5e-bfa1-f8e77c20939f_2300x588.png 1272w, https://substackcdn.com/image/fetch/$s_!KmiZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbb44985-a767-4f5e-bfa1-f8e77c20939f_2300x588.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Scenario 3.1: Borrow up to the loan limit</h3><h4>Business example</h4><p>Alice already borrowed 2 books and borrows one more.</p><h4>Gherkin</h4><pre><code><code>Scenario: Borrow up to the loan limit
  Given Alice already borrowed 2 books
  When Alice borrows another available book
  Then the library shows 3 borrowed books for Alice</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gjvS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2617e9ee-b39c-4950-bf9b-60bbe16e4782_1785x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gjvS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2617e9ee-b39c-4950-bf9b-60bbe16e4782_1785x188.png 424w, https://substackcdn.com/image/fetch/$s_!gjvS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2617e9ee-b39c-4950-bf9b-60bbe16e4782_1785x188.png 848w, https://substackcdn.com/image/fetch/$s_!gjvS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2617e9ee-b39c-4950-bf9b-60bbe16e4782_1785x188.png 1272w, https://substackcdn.com/image/fetch/$s_!gjvS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2617e9ee-b39c-4950-bf9b-60bbe16e4782_1785x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gjvS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2617e9ee-b39c-4950-bf9b-60bbe16e4782_1785x188.png" width="1456" height="153" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2617e9ee-b39c-4950-bf9b-60bbe16e4782_1785x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:153,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:32456,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2617e9ee-b39c-4950-bf9b-60bbe16e4782_1785x188.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_!gjvS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2617e9ee-b39c-4950-bf9b-60bbe16e4782_1785x188.png 424w, https://substackcdn.com/image/fetch/$s_!gjvS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2617e9ee-b39c-4950-bf9b-60bbe16e4782_1785x188.png 848w, https://substackcdn.com/image/fetch/$s_!gjvS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2617e9ee-b39c-4950-bf9b-60bbe16e4782_1785x188.png 1272w, https://substackcdn.com/image/fetch/$s_!gjvS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2617e9ee-b39c-4950-bf9b-60bbe16e4782_1785x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 3.2: Reject borrowing above the loan limit</h3><h4>Business example</h4><p>Alice already borrowed 3 books and cannot borrow a fourth.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject borrowing above the loan limit
  Given Alice already borrowed 3 books
  When Alice tries to borrow another book
  Then the borrowing request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Dzhk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac13eb94-8a03-425f-8180-dbae4b40f624_1792x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Dzhk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac13eb94-8a03-425f-8180-dbae4b40f624_1792x188.png 424w, https://substackcdn.com/image/fetch/$s_!Dzhk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac13eb94-8a03-425f-8180-dbae4b40f624_1792x188.png 848w, https://substackcdn.com/image/fetch/$s_!Dzhk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac13eb94-8a03-425f-8180-dbae4b40f624_1792x188.png 1272w, https://substackcdn.com/image/fetch/$s_!Dzhk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac13eb94-8a03-425f-8180-dbae4b40f624_1792x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Dzhk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac13eb94-8a03-425f-8180-dbae4b40f624_1792x188.png" width="1456" height="153" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ac13eb94-8a03-425f-8180-dbae4b40f624_1792x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:153,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29274,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac13eb94-8a03-425f-8180-dbae4b40f624_1792x188.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_!Dzhk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac13eb94-8a03-425f-8180-dbae4b40f624_1792x188.png 424w, https://substackcdn.com/image/fetch/$s_!Dzhk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac13eb94-8a03-425f-8180-dbae4b40f624_1792x188.png 848w, https://substackcdn.com/image/fetch/$s_!Dzhk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac13eb94-8a03-425f-8180-dbae4b40f624_1792x188.png 1272w, https://substackcdn.com/image/fetch/$s_!Dzhk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac13eb94-8a03-425f-8180-dbae4b40f624_1792x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 3.3: Keep the book available after exceeding the loan limit</h3><h4>Business example</h4><p>A failed borrowing request must not make the requested book unavailable.</p><h4>Gherkin</h4><pre><code><code>Scenario: Keep the book available after exceeding the loan limit
  Given Alice already borrowed 3 books
  When Alice tries to borrow "Refactoring"
  Then "Refactoring" remains available</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xdM6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11dc33be-7328-4976-90a0-099fd6e7fe62_2412x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xdM6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11dc33be-7328-4976-90a0-099fd6e7fe62_2412x188.png 424w, https://substackcdn.com/image/fetch/$s_!xdM6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11dc33be-7328-4976-90a0-099fd6e7fe62_2412x188.png 848w, https://substackcdn.com/image/fetch/$s_!xdM6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11dc33be-7328-4976-90a0-099fd6e7fe62_2412x188.png 1272w, https://substackcdn.com/image/fetch/$s_!xdM6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11dc33be-7328-4976-90a0-099fd6e7fe62_2412x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xdM6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11dc33be-7328-4976-90a0-099fd6e7fe62_2412x188.png" width="1456" height="113" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/11dc33be-7328-4976-90a0-099fd6e7fe62_2412x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:113,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:38956,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11dc33be-7328-4976-90a0-099fd6e7fe62_2412x188.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_!xdM6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11dc33be-7328-4976-90a0-099fd6e7fe62_2412x188.png 424w, https://substackcdn.com/image/fetch/$s_!xdM6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11dc33be-7328-4976-90a0-099fd6e7fe62_2412x188.png 848w, https://substackcdn.com/image/fetch/$s_!xdM6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11dc33be-7328-4976-90a0-099fd6e7fe62_2412x188.png 1272w, https://substackcdn.com/image/fetch/$s_!xdM6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11dc33be-7328-4976-90a0-099fd6e7fe62_2412x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Rule 4: A borrowed book cannot be borrowed again</h2><h3>What this rule means</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BSpI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda24b43c-0dd7-441e-af69-c578047ee749_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BSpI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda24b43c-0dd7-441e-af69-c578047ee749_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!BSpI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda24b43c-0dd7-441e-af69-c578047ee749_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!BSpI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda24b43c-0dd7-441e-af69-c578047ee749_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!BSpI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda24b43c-0dd7-441e-af69-c578047ee749_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BSpI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda24b43c-0dd7-441e-af69-c578047ee749_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/da24b43c-0dd7-441e-af69-c578047ee749_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:882075,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda24b43c-0dd7-441e-af69-c578047ee749_1024x559.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_!BSpI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda24b43c-0dd7-441e-af69-c578047ee749_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!BSpI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda24b43c-0dd7-441e-af69-c578047ee749_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!BSpI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda24b43c-0dd7-441e-af69-c578047ee749_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!BSpI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda24b43c-0dd7-441e-af69-c578047ee749_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A book borrowed by one member cannot be borrowed by another member.</p><p>The system must reject the second borrowing request and preserve the current borrower.</p><h3>Why this rule matters</h3><p>This rule protects the basic meaning of borrowing.</p><p>If the system allowed another member to borrow the same book, the library would no longer know who actually has it.</p><p>This rule also makes rejection more concrete. Rejecting an operation should not silently alter the previous valid state.</p><h3>Example</h3><p>Alice borrowed &#8220;Refactoring&#8221;.</p><p>Bob tries to borrow &#8220;Refactoring&#8221;.</p><p>The request is rejected.</p><h3>Counterexample</h3><p>Alice borrowed &#8220;Refactoring&#8221;.</p><p>Bob tries to borrow it, and the system changes the borrower from Alice to Bob.</p><p>That would be wrong because Bob&#8217;s request was invalid.</p><h3>What this rule adds to the kata</h3><p>The kata now checks that rejected operations preserve the previous valid state.</p><p>Rejection is not just an error result. It is also a promise that the existing library situation has not been damaged.</p><h3>Rule diagram</h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pgHJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd3bfc4-959f-4174-9a22-ccae9d43c557_2198x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pgHJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd3bfc4-959f-4174-9a22-ccae9d43c557_2198x188.png 424w, https://substackcdn.com/image/fetch/$s_!pgHJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd3bfc4-959f-4174-9a22-ccae9d43c557_2198x188.png 848w, https://substackcdn.com/image/fetch/$s_!pgHJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd3bfc4-959f-4174-9a22-ccae9d43c557_2198x188.png 1272w, https://substackcdn.com/image/fetch/$s_!pgHJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd3bfc4-959f-4174-9a22-ccae9d43c557_2198x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pgHJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd3bfc4-959f-4174-9a22-ccae9d43c557_2198x188.png" width="1456" height="125" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3cd3bfc4-959f-4174-9a22-ccae9d43c557_2198x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:125,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:35347,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd3bfc4-959f-4174-9a22-ccae9d43c557_2198x188.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_!pgHJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd3bfc4-959f-4174-9a22-ccae9d43c557_2198x188.png 424w, https://substackcdn.com/image/fetch/$s_!pgHJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd3bfc4-959f-4174-9a22-ccae9d43c557_2198x188.png 848w, https://substackcdn.com/image/fetch/$s_!pgHJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd3bfc4-959f-4174-9a22-ccae9d43c557_2198x188.png 1272w, https://substackcdn.com/image/fetch/$s_!pgHJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd3bfc4-959f-4174-9a22-ccae9d43c557_2198x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 4.1: Reject borrowing a book borrowed by another member</h3><h4>Business example</h4><p>Alice has already borrowed a book. Bob cannot borrow the same book.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject borrowing a book borrowed by another member
  Given Alice borrowed "Refactoring"
  When Bob tries to borrow "Refactoring"
  Then the borrowing request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zeY3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce3800a0-2a78-4419-8b01-6f8230e509da_1782x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zeY3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce3800a0-2a78-4419-8b01-6f8230e509da_1782x188.png 424w, https://substackcdn.com/image/fetch/$s_!zeY3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce3800a0-2a78-4419-8b01-6f8230e509da_1782x188.png 848w, https://substackcdn.com/image/fetch/$s_!zeY3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce3800a0-2a78-4419-8b01-6f8230e509da_1782x188.png 1272w, https://substackcdn.com/image/fetch/$s_!zeY3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce3800a0-2a78-4419-8b01-6f8230e509da_1782x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zeY3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce3800a0-2a78-4419-8b01-6f8230e509da_1782x188.png" width="1456" height="154" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ce3800a0-2a78-4419-8b01-6f8230e509da_1782x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:154,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:32341,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce3800a0-2a78-4419-8b01-6f8230e509da_1782x188.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_!zeY3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce3800a0-2a78-4419-8b01-6f8230e509da_1782x188.png 424w, https://substackcdn.com/image/fetch/$s_!zeY3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce3800a0-2a78-4419-8b01-6f8230e509da_1782x188.png 848w, https://substackcdn.com/image/fetch/$s_!zeY3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce3800a0-2a78-4419-8b01-6f8230e509da_1782x188.png 1272w, https://substackcdn.com/image/fetch/$s_!zeY3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce3800a0-2a78-4419-8b01-6f8230e509da_1782x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 4.2: Keep the current borrower after a rejected borrow</h3><h4>Business example</h4><p>Bob&#8217;s failed borrowing request must not replace Alice as the borrower.</p><h4>Gherkin</h4><pre><code><code>Scenario: Keep the current borrower after a rejected borrow
  Given Alice borrowed "Refactoring"
  When Bob tries to borrow "Refactoring"
  Then "Refactoring" is still borrowed by Alice</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8rel!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4583495-2f9e-43f6-9d7a-b6ccde87ffed_2412x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8rel!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4583495-2f9e-43f6-9d7a-b6ccde87ffed_2412x188.png 424w, https://substackcdn.com/image/fetch/$s_!8rel!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4583495-2f9e-43f6-9d7a-b6ccde87ffed_2412x188.png 848w, https://substackcdn.com/image/fetch/$s_!8rel!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4583495-2f9e-43f6-9d7a-b6ccde87ffed_2412x188.png 1272w, https://substackcdn.com/image/fetch/$s_!8rel!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4583495-2f9e-43f6-9d7a-b6ccde87ffed_2412x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8rel!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4583495-2f9e-43f6-9d7a-b6ccde87ffed_2412x188.png" width="1456" height="113" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d4583495-2f9e-43f6-9d7a-b6ccde87ffed_2412x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:113,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:40776,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4583495-2f9e-43f6-9d7a-b6ccde87ffed_2412x188.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_!8rel!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4583495-2f9e-43f6-9d7a-b6ccde87ffed_2412x188.png 424w, https://substackcdn.com/image/fetch/$s_!8rel!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4583495-2f9e-43f6-9d7a-b6ccde87ffed_2412x188.png 848w, https://substackcdn.com/image/fetch/$s_!8rel!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4583495-2f9e-43f6-9d7a-b6ccde87ffed_2412x188.png 1272w, https://substackcdn.com/image/fetch/$s_!8rel!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4583495-2f9e-43f6-9d7a-b6ccde87ffed_2412x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Rule 5: Borrowed books can be returned</h2><h3>What this rule means</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!e7RB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61e64b96-cb13-46b8-b9a3-95384626797b_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!e7RB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61e64b96-cb13-46b8-b9a3-95384626797b_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!e7RB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61e64b96-cb13-46b8-b9a3-95384626797b_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!e7RB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61e64b96-cb13-46b8-b9a3-95384626797b_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!e7RB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61e64b96-cb13-46b8-b9a3-95384626797b_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!e7RB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61e64b96-cb13-46b8-b9a3-95384626797b_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/61e64b96-cb13-46b8-b9a3-95384626797b_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:614223,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61e64b96-cb13-46b8-b9a3-95384626797b_1024x559.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_!e7RB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61e64b96-cb13-46b8-b9a3-95384626797b_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!e7RB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61e64b96-cb13-46b8-b9a3-95384626797b_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!e7RB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61e64b96-cb13-46b8-b9a3-95384626797b_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!e7RB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61e64b96-cb13-46b8-b9a3-95384626797b_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A borrowed book can be returned by the member who borrowed it.</p><p>After a valid return, the book can be borrowed again, and it no longer appears among the member&#8217;s borrowed books.</p><p>Invalid returns must be rejected.</p><h3>Why this rule matters</h3><p>Returning is not just the opposite of borrowing. It has its own conditions.</p><p>The book must actually be borrowed, and the person returning it must be the borrower.</p><p>This rule also makes the lifecycle of a borrowed book more complete: it can move from available, to borrowed, back to borrowable again.</p><h3>Example</h3><p>Alice borrowed &#8220;Refactoring&#8221;.</p><p>Alice returns it.</p><p>&#8220;Refactoring&#8221; can be borrowed again, and Alice no longer has it among her borrowed books.</p><h3>Counterexample</h3><p>Alice borrowed &#8220;Refactoring&#8221;.</p><p>Bob tries to return it.</p><p>The request is rejected because Bob is not the borrower.</p><h3>What this rule adds to the kata</h3><p>The kata now has a second operation that changes the same business reality.</p><p>Borrowing and returning must agree about what it means for a book to be borrowed, available, and associated with a member.</p><h3>Rule diagram</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8uKz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea51d9bb-759c-4dfb-a438-5eb532917410_2283x620.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8uKz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea51d9bb-759c-4dfb-a438-5eb532917410_2283x620.png 424w, https://substackcdn.com/image/fetch/$s_!8uKz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea51d9bb-759c-4dfb-a438-5eb532917410_2283x620.png 848w, https://substackcdn.com/image/fetch/$s_!8uKz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea51d9bb-759c-4dfb-a438-5eb532917410_2283x620.png 1272w, https://substackcdn.com/image/fetch/$s_!8uKz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea51d9bb-759c-4dfb-a438-5eb532917410_2283x620.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8uKz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea51d9bb-759c-4dfb-a438-5eb532917410_2283x620.png" width="1456" height="395" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ea51d9bb-759c-4dfb-a438-5eb532917410_2283x620.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:395,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:82342,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea51d9bb-759c-4dfb-a438-5eb532917410_2283x620.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_!8uKz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea51d9bb-759c-4dfb-a438-5eb532917410_2283x620.png 424w, https://substackcdn.com/image/fetch/$s_!8uKz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea51d9bb-759c-4dfb-a438-5eb532917410_2283x620.png 848w, https://substackcdn.com/image/fetch/$s_!8uKz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea51d9bb-759c-4dfb-a438-5eb532917410_2283x620.png 1272w, https://substackcdn.com/image/fetch/$s_!8uKz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea51d9bb-759c-4dfb-a438-5eb532917410_2283x620.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Scenario 5.1: Return one borrowed book</h3><h4>Business example</h4><p>Alice returns a book she borrowed.</p><h4>Gherkin</h4><pre><code><code>Scenario: Return one borrowed book
  Given Alice borrowed "Refactoring"
  When Alice returns "Refactoring"
  Then "Refactoring" can be borrowed again</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nod5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee818346-c1a5-4ca6-ba44-2a29883d7adf_2283x620.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nod5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee818346-c1a5-4ca6-ba44-2a29883d7adf_2283x620.png 424w, https://substackcdn.com/image/fetch/$s_!nod5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee818346-c1a5-4ca6-ba44-2a29883d7adf_2283x620.png 848w, https://substackcdn.com/image/fetch/$s_!nod5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee818346-c1a5-4ca6-ba44-2a29883d7adf_2283x620.png 1272w, https://substackcdn.com/image/fetch/$s_!nod5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee818346-c1a5-4ca6-ba44-2a29883d7adf_2283x620.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nod5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee818346-c1a5-4ca6-ba44-2a29883d7adf_2283x620.png" width="1456" height="395" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee818346-c1a5-4ca6-ba44-2a29883d7adf_2283x620.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:395,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:82342,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee818346-c1a5-4ca6-ba44-2a29883d7adf_2283x620.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_!nod5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee818346-c1a5-4ca6-ba44-2a29883d7adf_2283x620.png 424w, https://substackcdn.com/image/fetch/$s_!nod5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee818346-c1a5-4ca6-ba44-2a29883d7adf_2283x620.png 848w, https://substackcdn.com/image/fetch/$s_!nod5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee818346-c1a5-4ca6-ba44-2a29883d7adf_2283x620.png 1272w, https://substackcdn.com/image/fetch/$s_!nod5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee818346-c1a5-4ca6-ba44-2a29883d7adf_2283x620.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Scenario 5.2: Remove a returned book from the member&#8217;s borrowed books</h3><h4>Business example</h4><p>After returning a book, Alice should no longer see it among her borrowed books.</p><h4>Gherkin</h4><pre><code><code>Scenario: Remove a returned book from the member's borrowed books
  Given Alice borrowed "Refactoring"
  When Alice returns "Refactoring"
  Then the library shows no borrowed books for Alice</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!g9Gw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf5003d1-4c71-4bee-876c-79a2b666cd8e_1739x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!g9Gw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf5003d1-4c71-4bee-876c-79a2b666cd8e_1739x188.png 424w, https://substackcdn.com/image/fetch/$s_!g9Gw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf5003d1-4c71-4bee-876c-79a2b666cd8e_1739x188.png 848w, https://substackcdn.com/image/fetch/$s_!g9Gw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf5003d1-4c71-4bee-876c-79a2b666cd8e_1739x188.png 1272w, https://substackcdn.com/image/fetch/$s_!g9Gw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf5003d1-4c71-4bee-876c-79a2b666cd8e_1739x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!g9Gw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf5003d1-4c71-4bee-876c-79a2b666cd8e_1739x188.png" width="1456" height="157" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/df5003d1-4c71-4bee-876c-79a2b666cd8e_1739x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:157,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28442,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf5003d1-4c71-4bee-876c-79a2b666cd8e_1739x188.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_!g9Gw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf5003d1-4c71-4bee-876c-79a2b666cd8e_1739x188.png 424w, https://substackcdn.com/image/fetch/$s_!g9Gw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf5003d1-4c71-4bee-876c-79a2b666cd8e_1739x188.png 848w, https://substackcdn.com/image/fetch/$s_!g9Gw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf5003d1-4c71-4bee-876c-79a2b666cd8e_1739x188.png 1272w, https://substackcdn.com/image/fetch/$s_!g9Gw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf5003d1-4c71-4bee-876c-79a2b666cd8e_1739x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 5.3: Reject returning a book that is not borrowed</h3><h4>Business example</h4><p>Alice cannot return a book that nobody borrowed.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject returning a book that is not borrowed
  Given "Refactoring" is available
  When Alice tries to return "Refactoring"
  Then the return request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xiDM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd2c8c50-aabe-46a5-b32a-b39274c26f64_1619x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xiDM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd2c8c50-aabe-46a5-b32a-b39274c26f64_1619x140.png 424w, https://substackcdn.com/image/fetch/$s_!xiDM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd2c8c50-aabe-46a5-b32a-b39274c26f64_1619x140.png 848w, https://substackcdn.com/image/fetch/$s_!xiDM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd2c8c50-aabe-46a5-b32a-b39274c26f64_1619x140.png 1272w, https://substackcdn.com/image/fetch/$s_!xiDM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd2c8c50-aabe-46a5-b32a-b39274c26f64_1619x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xiDM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd2c8c50-aabe-46a5-b32a-b39274c26f64_1619x140.png" width="1456" height="126" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dd2c8c50-aabe-46a5-b32a-b39274c26f64_1619x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:126,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19804,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd2c8c50-aabe-46a5-b32a-b39274c26f64_1619x140.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_!xiDM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd2c8c50-aabe-46a5-b32a-b39274c26f64_1619x140.png 424w, https://substackcdn.com/image/fetch/$s_!xiDM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd2c8c50-aabe-46a5-b32a-b39274c26f64_1619x140.png 848w, https://substackcdn.com/image/fetch/$s_!xiDM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd2c8c50-aabe-46a5-b32a-b39274c26f64_1619x140.png 1272w, https://substackcdn.com/image/fetch/$s_!xiDM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd2c8c50-aabe-46a5-b32a-b39274c26f64_1619x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 5.4: Reject returning a book borrowed by another member</h3><h4>Business example</h4><p>Bob cannot return a book borrowed by Alice.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject returning a book borrowed by another member
  Given Alice borrowed "Refactoring"
  When Bob tries to return "Refactoring"
  Then the return request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!J2Nn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89c6dbba-6e25-4eba-b082-ad264411489e_1667x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!J2Nn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89c6dbba-6e25-4eba-b082-ad264411489e_1667x188.png 424w, https://substackcdn.com/image/fetch/$s_!J2Nn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89c6dbba-6e25-4eba-b082-ad264411489e_1667x188.png 848w, https://substackcdn.com/image/fetch/$s_!J2Nn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89c6dbba-6e25-4eba-b082-ad264411489e_1667x188.png 1272w, https://substackcdn.com/image/fetch/$s_!J2Nn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89c6dbba-6e25-4eba-b082-ad264411489e_1667x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!J2Nn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89c6dbba-6e25-4eba-b082-ad264411489e_1667x188.png" width="1456" height="164" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/89c6dbba-6e25-4eba-b082-ad264411489e_1667x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:164,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26870,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89c6dbba-6e25-4eba-b082-ad264411489e_1667x188.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_!J2Nn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89c6dbba-6e25-4eba-b082-ad264411489e_1667x188.png 424w, https://substackcdn.com/image/fetch/$s_!J2Nn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89c6dbba-6e25-4eba-b082-ad264411489e_1667x188.png 848w, https://substackcdn.com/image/fetch/$s_!J2Nn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89c6dbba-6e25-4eba-b082-ad264411489e_1667x188.png 1272w, https://substackcdn.com/image/fetch/$s_!J2Nn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89c6dbba-6e25-4eba-b082-ad264411489e_1667x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Rule 6: Borrowed books can be reserved</h2><h3>What this rule means</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Nq3F!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb5721-f700-45b5-a3c8-5ec764a92b62_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Nq3F!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb5721-f700-45b5-a3c8-5ec764a92b62_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Nq3F!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb5721-f700-45b5-a3c8-5ec764a92b62_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Nq3F!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb5721-f700-45b5-a3c8-5ec764a92b62_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Nq3F!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb5721-f700-45b5-a3c8-5ec764a92b62_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Nq3F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb5721-f700-45b5-a3c8-5ec764a92b62_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/32fb5721-f700-45b5-a3c8-5ec764a92b62_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:631852,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb5721-f700-45b5-a3c8-5ec764a92b62_1024x559.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_!Nq3F!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb5721-f700-45b5-a3c8-5ec764a92b62_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Nq3F!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb5721-f700-45b5-a3c8-5ec764a92b62_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Nq3F!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb5721-f700-45b5-a3c8-5ec764a92b62_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Nq3F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb5721-f700-45b5-a3c8-5ec764a92b62_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A member can reserve a book only when another member has borrowed it.</p><p>Reserving means the member is waiting for that book.</p><p>A member cannot reserve an available book because they can borrow it directly. A member also cannot reserve a book they already borrowed.</p><h3>Why this rule matters</h3><p>Reservation introduces future intent.</p><p>The book is still borrowed now, but the library must remember that someone else is waiting for it.</p><p>This changes the meaning of a later return.</p><h3>Example</h3><p>Alice borrowed &#8220;Refactoring&#8221;.</p><p>Bob reserves it.</p><p>Bob is now waiting for &#8220;Refactoring&#8221;.</p><h3>Counterexample</h3><p>&#8220;Refactoring&#8221; is available.</p><p>Bob tries to reserve it.</p><p>The request is rejected because available books should be borrowed, not reserved.</p><h3>What this rule adds to the kata</h3><p>The kata now has behaviour that affects a future operation.</p><p>Reserving a book does not make it available immediately, but it changes what should happen when the borrower returns it.</p><h3>Rule diagram</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!U4PU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f6b2f-b4d3-44d3-b5d5-19949c5c43ad_1757x588.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!U4PU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f6b2f-b4d3-44d3-b5d5-19949c5c43ad_1757x588.png 424w, https://substackcdn.com/image/fetch/$s_!U4PU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f6b2f-b4d3-44d3-b5d5-19949c5c43ad_1757x588.png 848w, https://substackcdn.com/image/fetch/$s_!U4PU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f6b2f-b4d3-44d3-b5d5-19949c5c43ad_1757x588.png 1272w, https://substackcdn.com/image/fetch/$s_!U4PU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f6b2f-b4d3-44d3-b5d5-19949c5c43ad_1757x588.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!U4PU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f6b2f-b4d3-44d3-b5d5-19949c5c43ad_1757x588.png" width="1456" height="487" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/150f6b2f-b4d3-44d3-b5d5-19949c5c43ad_1757x588.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:487,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:63279,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f6b2f-b4d3-44d3-b5d5-19949c5c43ad_1757x588.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_!U4PU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f6b2f-b4d3-44d3-b5d5-19949c5c43ad_1757x588.png 424w, https://substackcdn.com/image/fetch/$s_!U4PU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f6b2f-b4d3-44d3-b5d5-19949c5c43ad_1757x588.png 848w, https://substackcdn.com/image/fetch/$s_!U4PU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f6b2f-b4d3-44d3-b5d5-19949c5c43ad_1757x588.png 1272w, https://substackcdn.com/image/fetch/$s_!U4PU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F150f6b2f-b4d3-44d3-b5d5-19949c5c43ad_1757x588.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Scenario 6.1: Reserve a borrowed book</h3><h4>Business example</h4><p>Alice has borrowed a book. Bob wants to wait for it.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reserve a borrowed book
  Given Alice borrowed "Refactoring"
  When Bob reserves "Refactoring"
  Then Bob is waiting for "Refactoring"</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RB1Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c55b318-ef78-4359-80f3-86db6b1002b7_1710x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RB1Y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c55b318-ef78-4359-80f3-86db6b1002b7_1710x188.png 424w, https://substackcdn.com/image/fetch/$s_!RB1Y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c55b318-ef78-4359-80f3-86db6b1002b7_1710x188.png 848w, https://substackcdn.com/image/fetch/$s_!RB1Y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c55b318-ef78-4359-80f3-86db6b1002b7_1710x188.png 1272w, https://substackcdn.com/image/fetch/$s_!RB1Y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c55b318-ef78-4359-80f3-86db6b1002b7_1710x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RB1Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c55b318-ef78-4359-80f3-86db6b1002b7_1710x188.png" width="1456" height="160" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6c55b318-ef78-4359-80f3-86db6b1002b7_1710x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:160,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28994,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c55b318-ef78-4359-80f3-86db6b1002b7_1710x188.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_!RB1Y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c55b318-ef78-4359-80f3-86db6b1002b7_1710x188.png 424w, https://substackcdn.com/image/fetch/$s_!RB1Y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c55b318-ef78-4359-80f3-86db6b1002b7_1710x188.png 848w, https://substackcdn.com/image/fetch/$s_!RB1Y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c55b318-ef78-4359-80f3-86db6b1002b7_1710x188.png 1272w, https://substackcdn.com/image/fetch/$s_!RB1Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c55b318-ef78-4359-80f3-86db6b1002b7_1710x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 6.2: Reject reserving an available book</h3><h4>Business example</h4><p>Bob should borrow an available book, not reserve it.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject reserving an available book
  Given "Refactoring" is available
  When Bob tries to reserve "Refactoring"
  Then the reservation request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YyyP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9c6b8ad-e645-4baa-8c7b-fbfc115f135f_1640x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YyyP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9c6b8ad-e645-4baa-8c7b-fbfc115f135f_1640x188.png 424w, https://substackcdn.com/image/fetch/$s_!YyyP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9c6b8ad-e645-4baa-8c7b-fbfc115f135f_1640x188.png 848w, https://substackcdn.com/image/fetch/$s_!YyyP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9c6b8ad-e645-4baa-8c7b-fbfc115f135f_1640x188.png 1272w, https://substackcdn.com/image/fetch/$s_!YyyP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9c6b8ad-e645-4baa-8c7b-fbfc115f135f_1640x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YyyP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9c6b8ad-e645-4baa-8c7b-fbfc115f135f_1640x188.png" width="1456" height="167" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d9c6b8ad-e645-4baa-8c7b-fbfc115f135f_1640x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:167,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26078,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9c6b8ad-e645-4baa-8c7b-fbfc115f135f_1640x188.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_!YyyP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9c6b8ad-e645-4baa-8c7b-fbfc115f135f_1640x188.png 424w, https://substackcdn.com/image/fetch/$s_!YyyP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9c6b8ad-e645-4baa-8c7b-fbfc115f135f_1640x188.png 848w, https://substackcdn.com/image/fetch/$s_!YyyP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9c6b8ad-e645-4baa-8c7b-fbfc115f135f_1640x188.png 1272w, https://substackcdn.com/image/fetch/$s_!YyyP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9c6b8ad-e645-4baa-8c7b-fbfc115f135f_1640x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 6.3: Reject reserving a book borrowed by the same member</h3><h4>Business example</h4><p>Alice cannot reserve a book she already borrowed.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject reserving a book borrowed by the same member
  Given Alice borrowed "Refactoring"
  When Alice tries to reserve "Refactoring"
  Then the reservation request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!w5zD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c53525-62e2-4482-a0de-4c9d6ae49c7d_1782x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!w5zD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c53525-62e2-4482-a0de-4c9d6ae49c7d_1782x188.png 424w, https://substackcdn.com/image/fetch/$s_!w5zD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c53525-62e2-4482-a0de-4c9d6ae49c7d_1782x188.png 848w, https://substackcdn.com/image/fetch/$s_!w5zD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c53525-62e2-4482-a0de-4c9d6ae49c7d_1782x188.png 1272w, https://substackcdn.com/image/fetch/$s_!w5zD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c53525-62e2-4482-a0de-4c9d6ae49c7d_1782x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!w5zD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c53525-62e2-4482-a0de-4c9d6ae49c7d_1782x188.png" width="1456" height="154" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/42c53525-62e2-4482-a0de-4c9d6ae49c7d_1782x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:154,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31783,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c53525-62e2-4482-a0de-4c9d6ae49c7d_1782x188.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_!w5zD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c53525-62e2-4482-a0de-4c9d6ae49c7d_1782x188.png 424w, https://substackcdn.com/image/fetch/$s_!w5zD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c53525-62e2-4482-a0de-4c9d6ae49c7d_1782x188.png 848w, https://substackcdn.com/image/fetch/$s_!w5zD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c53525-62e2-4482-a0de-4c9d6ae49c7d_1782x188.png 1272w, https://substackcdn.com/image/fetch/$s_!w5zD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42c53525-62e2-4482-a0de-4c9d6ae49c7d_1782x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Rule 7: Reservations affect returns</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qPJ9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d0327a1-475b-4195-878d-6681a0256fe2_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qPJ9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d0327a1-475b-4195-878d-6681a0256fe2_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!qPJ9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d0327a1-475b-4195-878d-6681a0256fe2_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!qPJ9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d0327a1-475b-4195-878d-6681a0256fe2_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!qPJ9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d0327a1-475b-4195-878d-6681a0256fe2_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qPJ9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d0327a1-475b-4195-878d-6681a0256fe2_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4d0327a1-475b-4195-878d-6681a0256fe2_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:642580,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d0327a1-475b-4195-878d-6681a0256fe2_1024x559.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_!qPJ9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d0327a1-475b-4195-878d-6681a0256fe2_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!qPJ9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d0327a1-475b-4195-878d-6681a0256fe2_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!qPJ9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d0327a1-475b-4195-878d-6681a0256fe2_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!qPJ9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d0327a1-475b-4195-878d-6681a0256fe2_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>What this rule means</h3><p>When a reserved book is returned, it does not become generally available to everyone.</p><p>It becomes available to the waiting member.</p><p>That waiting member has priority. Other members cannot take the book first.</p><h3>Why this rule matters</h3><p>This is the key rule in the kata. It changes the meaning of &#8220;return&#8221;.</p><p>Before reservations, returning a book simply made it available. After reservations, returning a book may make it available only to a specific member.</p><h3>Example</h3><p>Alice borrowed &#8220;Refactoring&#8221;.</p><p>Bob is waiting for it.</p><p>Alice returns it.</p><p>&#8220;Refactoring&#8221; is now available to Bob.</p><h3>Counterexample</h3><p>Alice returns &#8220;Refactoring&#8221;, which Bob was waiting for.</p><p>Carol tries to borrow it before Bob.</p><p>The request is rejected, and the book remains available to Bob.</p><h3>What this rule adds to the kata</h3><p>The kata now forces the reader to think about aftereffects more carefully.</p><p>The same operation, returning a book, can have different outcomes depending on the business situation.</p><h3>Rule diagram</h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!deJZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96ec8542-e007-473b-9dcd-03f9b8331803_2787x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!deJZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96ec8542-e007-473b-9dcd-03f9b8331803_2787x188.png 424w, https://substackcdn.com/image/fetch/$s_!deJZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96ec8542-e007-473b-9dcd-03f9b8331803_2787x188.png 848w, https://substackcdn.com/image/fetch/$s_!deJZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96ec8542-e007-473b-9dcd-03f9b8331803_2787x188.png 1272w, https://substackcdn.com/image/fetch/$s_!deJZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96ec8542-e007-473b-9dcd-03f9b8331803_2787x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!deJZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96ec8542-e007-473b-9dcd-03f9b8331803_2787x188.png" width="1456" height="98" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/96ec8542-e007-473b-9dcd-03f9b8331803_2787x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:98,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:44471,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96ec8542-e007-473b-9dcd-03f9b8331803_2787x188.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_!deJZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96ec8542-e007-473b-9dcd-03f9b8331803_2787x188.png 424w, https://substackcdn.com/image/fetch/$s_!deJZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96ec8542-e007-473b-9dcd-03f9b8331803_2787x188.png 848w, https://substackcdn.com/image/fetch/$s_!deJZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96ec8542-e007-473b-9dcd-03f9b8331803_2787x188.png 1272w, https://substackcdn.com/image/fetch/$s_!deJZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96ec8542-e007-473b-9dcd-03f9b8331803_2787x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 7.1: Return a book with one waiting member</h3><h4>Business example</h4><p>Alice returns a book Bob was waiting for.</p><h4>Gherkin</h4><pre><code><code>Scenario: Return a book with one waiting member
  Given Alice borrowed "Refactoring"
  And Bob is waiting for "Refactoring"
  When Alice returns "Refactoring"
  Then "Refactoring" is available to Bob</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LQFC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F372dffae-b0f7-4bbb-860c-a9cbdd126451_2369x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LQFC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F372dffae-b0f7-4bbb-860c-a9cbdd126451_2369x188.png 424w, https://substackcdn.com/image/fetch/$s_!LQFC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F372dffae-b0f7-4bbb-860c-a9cbdd126451_2369x188.png 848w, https://substackcdn.com/image/fetch/$s_!LQFC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F372dffae-b0f7-4bbb-860c-a9cbdd126451_2369x188.png 1272w, https://substackcdn.com/image/fetch/$s_!LQFC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F372dffae-b0f7-4bbb-860c-a9cbdd126451_2369x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LQFC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F372dffae-b0f7-4bbb-860c-a9cbdd126451_2369x188.png" width="1456" height="116" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/372dffae-b0f7-4bbb-860c-a9cbdd126451_2369x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:116,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:39815,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F372dffae-b0f7-4bbb-860c-a9cbdd126451_2369x188.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_!LQFC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F372dffae-b0f7-4bbb-860c-a9cbdd126451_2369x188.png 424w, https://substackcdn.com/image/fetch/$s_!LQFC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F372dffae-b0f7-4bbb-860c-a9cbdd126451_2369x188.png 848w, https://substackcdn.com/image/fetch/$s_!LQFC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F372dffae-b0f7-4bbb-860c-a9cbdd126451_2369x188.png 1272w, https://substackcdn.com/image/fetch/$s_!LQFC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F372dffae-b0f7-4bbb-860c-a9cbdd126451_2369x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 7.2: Allow the waiting member to borrow the returned book</h3><h4>Business example</h4><p>Bob can borrow the book that was returned for him.</p><h4>Gherkin</h4><pre><code><code>Scenario: Allow the waiting member to borrow the returned book
  Given "Refactoring" is available to Bob
  When Bob borrows "Refactoring"
  Then "Refactoring" is borrowed by Bob</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EzUU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d237a28-4c59-4de4-8414-4e4d77b1c582_1742x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EzUU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d237a28-4c59-4de4-8414-4e4d77b1c582_1742x188.png 424w, https://substackcdn.com/image/fetch/$s_!EzUU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d237a28-4c59-4de4-8414-4e4d77b1c582_1742x188.png 848w, https://substackcdn.com/image/fetch/$s_!EzUU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d237a28-4c59-4de4-8414-4e4d77b1c582_1742x188.png 1272w, https://substackcdn.com/image/fetch/$s_!EzUU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d237a28-4c59-4de4-8414-4e4d77b1c582_1742x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EzUU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d237a28-4c59-4de4-8414-4e4d77b1c582_1742x188.png" width="1456" height="157" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7d237a28-4c59-4de4-8414-4e4d77b1c582_1742x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:157,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28526,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d237a28-4c59-4de4-8414-4e4d77b1c582_1742x188.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_!EzUU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d237a28-4c59-4de4-8414-4e4d77b1c582_1742x188.png 424w, https://substackcdn.com/image/fetch/$s_!EzUU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d237a28-4c59-4de4-8414-4e4d77b1c582_1742x188.png 848w, https://substackcdn.com/image/fetch/$s_!EzUU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d237a28-4c59-4de4-8414-4e4d77b1c582_1742x188.png 1272w, https://substackcdn.com/image/fetch/$s_!EzUU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d237a28-4c59-4de4-8414-4e4d77b1c582_1742x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 7.3: Reject borrowing a returned book reserved for another member</h3><h4>Business example</h4><p>Carol cannot borrow a returned book that is waiting for Bob.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject borrowing a returned book reserved for another member
  Given "Refactoring" is available to Bob
  When Carol tries to borrow "Refactoring"
  Then the borrowing request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E5H5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8093908b-c873-43a6-a2a4-fa439a343d69_1792x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E5H5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8093908b-c873-43a6-a2a4-fa439a343d69_1792x188.png 424w, https://substackcdn.com/image/fetch/$s_!E5H5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8093908b-c873-43a6-a2a4-fa439a343d69_1792x188.png 848w, https://substackcdn.com/image/fetch/$s_!E5H5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8093908b-c873-43a6-a2a4-fa439a343d69_1792x188.png 1272w, https://substackcdn.com/image/fetch/$s_!E5H5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8093908b-c873-43a6-a2a4-fa439a343d69_1792x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!E5H5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8093908b-c873-43a6-a2a4-fa439a343d69_1792x188.png" width="1456" height="153" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8093908b-c873-43a6-a2a4-fa439a343d69_1792x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:153,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:30130,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8093908b-c873-43a6-a2a4-fa439a343d69_1792x188.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_!E5H5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8093908b-c873-43a6-a2a4-fa439a343d69_1792x188.png 424w, https://substackcdn.com/image/fetch/$s_!E5H5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8093908b-c873-43a6-a2a4-fa439a343d69_1792x188.png 848w, https://substackcdn.com/image/fetch/$s_!E5H5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8093908b-c873-43a6-a2a4-fa439a343d69_1792x188.png 1272w, https://substackcdn.com/image/fetch/$s_!E5H5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8093908b-c873-43a6-a2a4-fa439a343d69_1792x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 7.4: Keep the reservation after a rejected borrow by another member</h3><h4>Business example</h4><p>Carol&#8217;s failed borrowing request must not remove Bob&#8217;s priority.</p><h4>Gherkin</h4><pre><code><code>Scenario: Keep the reservation after a rejected borrow by another member
  Given "Refactoring" is available to Bob
  When Carol tries to borrow "Refactoring"
  Then "Refactoring" remains available to Bob</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CJAt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F369f2a64-6a83-4e6a-b041-dba8588a08a1_2412x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CJAt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F369f2a64-6a83-4e6a-b041-dba8588a08a1_2412x188.png 424w, https://substackcdn.com/image/fetch/$s_!CJAt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F369f2a64-6a83-4e6a-b041-dba8588a08a1_2412x188.png 848w, https://substackcdn.com/image/fetch/$s_!CJAt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F369f2a64-6a83-4e6a-b041-dba8588a08a1_2412x188.png 1272w, https://substackcdn.com/image/fetch/$s_!CJAt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F369f2a64-6a83-4e6a-b041-dba8588a08a1_2412x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CJAt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F369f2a64-6a83-4e6a-b041-dba8588a08a1_2412x188.png" width="1456" height="113" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/369f2a64-6a83-4e6a-b041-dba8588a08a1_2412x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:113,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:39288,&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://emmanuelvalverderamos.substack.com/i/196391513?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F369f2a64-6a83-4e6a-b041-dba8588a08a1_2412x188.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_!CJAt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F369f2a64-6a83-4e6a-b041-dba8588a08a1_2412x188.png 424w, https://substackcdn.com/image/fetch/$s_!CJAt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F369f2a64-6a83-4e6a-b041-dba8588a08a1_2412x188.png 848w, https://substackcdn.com/image/fetch/$s_!CJAt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F369f2a64-6a83-4e6a-b041-dba8588a08a1_2412x188.png 1272w, https://substackcdn.com/image/fetch/$s_!CJAt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F369f2a64-6a83-4e6a-b041-dba8588a08a1_2412x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Your dependencies are part of your production system]]></title><description><![CDATA[Dependency-Track turns your SBOM into a living map of software supply-chain risk]]></description><link>https://emmanuelvalverderamos.substack.com/p/your-dependencies-are-part-of-your</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/your-dependencies-are-part-of-your</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 04 Aug 2026 06:00:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!3QK9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8440f0db-6b07-4526-8e6c-53208a891184_1679x606.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most teams have a clearer view of their source code than of the software they actually run. They know the repository, the main language, the framework, the deployment target, and probably the pipeline that ships it. But once a vulnerability, malicious package, license problem, or compromised build dependency appears, that knowledge is not enough. The relevant question is no longer whether the team uses a dependency in a general sense. The relevant question is where that dependency exists, which versions are affected, which products include it transitively, which teams own those products, which environments received the affected build, and what decisions have already been made about the risk.</p><p>This is where many organizations discover that dependency management is not really a system. It is often a collection of local habits. One team relies on package manager audits, another one checks GitHub alerts, another one uses a commercial scanner, another one exports a spreadsheet when compliance asks, and older internal projects may have no current dependency visibility at all. That can feel manageable while nothing happens. It becomes expensive when a real supply-chain incident forces the organization to answer portfolio-level questions quickly.</p><p>The TanStack npm supply-chain compromise is a useful example because it was not only a story about a malicious package version. The postmortem describes a chain that involved CI trust boundaries, GitHub Actions cache poisoning, OIDC token extraction, malicious package publishing, and payload execution during package installation. That matters because modern software delivery has more execution surfaces than production alone. Code can run on developer machines, CI runners, package installation hooks, test environments, release workflows, container builds, deployment automation, and supporting tools. Some of those places also hold credentials, tokens, caches, and publishing permissions.</p><p>Dependency-Track does not solve all of that. It will not secure a GitHub Actions workflow by itself, rotate secrets, validate every maintainer account, or prevent a malicious package from being published. Its value is different. It gives the organization a continuously updated inventory of what its software is made of, where each component is used, which risks are associated with those components, how those risks are being triaged, and which policies are being violated across the portfolio.</p><p>That is why Dependency-Track is so relevant for DevEx. Good developer experience is not only about making the happy path fast. It is about making the safe path easy, visible, repeatable, and boring. A developer should not need to become a supply-chain investigator every time a new CVE appears. The delivery system should generate the evidence, upload it automatically, analyze it centrally, and route useful feedback to the right people.</p><h2>TL;DR</h2><p>Dependency-Track is an OWASP software supply-chain component analysis platform built around SBOMs, especially CycloneDX</p><p>Its main value is not scanning one repository once, but continuously monitoring the components used across projects and versions</p><p>It helps teams answer impact questions when a vulnerability, malicious package, license problem, or operational policy violation appears</p><p>It works well in CI/CD because pipelines can generate SBOMs and upload them automatically after build, packaging, or release</p><p>It is ecosystem agnostic, which makes it useful for organizations with multiple languages, package managers, container images, services, and internal libraries</p><p>It supports vulnerability intelligence, policy compliance, triage workflows, notifications, REST API automation, OpenAPI documentation, and integrations with delivery and collaboration tools</p><p>It should not be treated as a replacement for secure CI, dependency update discipline, package provenance, secret hygiene, incident response, or engineering judgment</p><p>Taskfile improves the workflow because it turns SBOM generation, validation, upload, health checks, local experimentation and CI integration into repeatable commands</p><p>Its strongest DevEx value is reducing manual discovery work, lowering coordination cost, and making supply-chain risk visible before the next emergency</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Index</h2><ol><li><p>The real problem Dependency-Track solves</p></li><li><p>Why SBOM-first changes the conversation</p></li><li><p>What the TanStack incident teaches us</p></li><li><p>What Dependency-Track is</p></li><li><p>What Dependency-Track is not</p></li><li><p>Core concepts</p></li><li><p>How the platform works</p></li><li><p>Installation options</p></li><li><p>First local setup with Docker Compose</p></li><li><p>Production setup considerations</p></li><li><p>Kubernetes deployment</p></li><li><p>Creating projects</p></li><li><p>Generating SBOMs</p></li><li><p>Uploading SBOMs</p></li><li><p>CI/CD integration</p></li><li><p>GitHub Actions example</p></li><li><p>Jenkins approach</p></li><li><p>Working with multiple ecosystems</p></li><li><p>Improving DevEx with Taskfile</p></li><li><p>Policies that actually help teams</p></li><li><p>Triage workflow</p></li><li><p>Notifications</p></li><li><p>Dashboards and portfolio visibility</p></li><li><p>REST API and automation</p></li><li><p>Security patterns around Dependency-Track</p></li><li><p>How to use it during an incident</p></li><li><p>How it helps with supply-chain attacks</p></li><li><p>Common mistakes</p></li><li><p>Adoption roadmap</p></li><li><p>Conclusions</p></li><li><p>References</p></li></ol><h2>1. The real problem Dependency-Track solves</h2><p>The problem is not that teams use dependencies. Modern software is dependency-shaped. A small frontend can include hundreds or thousands of packages once transitive dependencies are counted. A backend service may depend on frameworks, logging libraries, serialization libraries, database drivers, build plugins, test libraries, container base images, operating system packages, CI actions, deployment tools, and internal shared libraries. Even if a team writes a modest amount of application code, the delivered system is usually assembled from a large amount of external and internal software.</p><p>The real problem is that dependency knowledge is usually trapped in places that are not designed to answer organizational questions. A lockfile can tell one repository what it resolved at one moment. A package manager audit can warn one project about known issues. A vulnerability scanner can raise findings. A spreadsheet can satisfy a compliance request once. A ticket can track a remediation task. None of these, by themselves, gives the organization a living view of component usage across the software portfolio.</p><p>Supply-chain incidents do not respect repository boundaries. When a package is compromised, deprecated, vulnerable, legally risky, modified, or operationally forbidden, the organization needs to know where it appears. That means direct dependencies, transitive dependencies, released versions, active branches, deployed containers, internal libraries, and sometimes build-time tools. Dependency-Track helps by turning SBOMs into a central inventory that can be analyzed continuously.</p><p>This is the core value of the tool. Dependency-Track is not merely a scanner. It is an inventory, analysis, policy, triage, notification, and automation platform for software composition. It helps teams move from local dependency awareness to portfolio-level dependency intelligence.</p><h2>2. Why SBOM-first changes the conversation</h2><p>A Software Bill of Materials is an inventory of the components that make up a software system. The idea sounds simple, but the operational impact is significant. Without an SBOM, every supply-chain question starts with discovery. With an SBOM, discovery becomes a maintained asset that can be queried when new information appears.</p><p>That distinction matters because incidents punish slow discovery. If an organization first has to identify every service, ask every team, inspect every repository, infer every transitive dependency, and manually compare versions, response time is constrained by organizational memory. The more systems the organization owns, the more expensive this becomes.</p><p>An SBOM-first operating model changes the flow. The build generates evidence. The pipeline uploads that evidence. Dependency-Track stores and analyzes it. New vulnerability intelligence, license data, or policy rules can then be matched against existing projects without requiring every team to manually rebuild its understanding of the dependency graph.</p><p>This is why Dependency-Track is more powerful than a one-time dependency scan. A scan answers a moment. A continuously updated SBOM portfolio answers change. That is the difference between a report and an operational capability.</p><h2>3. What the TanStack incident teaches us</h2><p>The TanStack npm supply-chain compromise is important because it shows how dependency risk can cross boundaries between source control, CI automation, package publishing, package installation, and credential exposure. The attack was not simply a vulnerable library being used in production. It involved trust relationships inside the delivery system.</p><p>The postmortem describes how a workflow pattern, cache poisoning, and token extraction contributed to malicious package publication. The payload could then run during package installation and attempt to collect credentials from common developer and CI locations. This is an uncomfortable reminder that installation can be execution, and that CI is part of the trusted computing base of modern software delivery.</p><p>Dependency-Track helps with one part of this situation: impact analysis. If affected package names and versions are known, a team can use Dependency-Track to identify where those components appear across the portfolio, which projects include them, and which owners need to respond. That does not replace secret rotation, runner hardening, workflow review, package provenance, or incident response. But it can reduce the time between external intelligence and internal understanding.</p><p>That time matters. During a supply-chain incident, the first cost is often not remediation. The first cost is finding out whether the organization is affected at all.</p><h2>4. What Dependency-Track is</h2><p>Dependency-Track is an OWASP component analysis platform focused on software supply-chain risk. Its normal workflow is straightforward: teams generate SBOMs for their software, upload those SBOMs to Dependency-Track, and the platform correlates the listed components with vulnerability intelligence, license information, policy rules, and project metadata.</p><p>The platform is API-first, which is important because the best way to use it is through automation. Developers should not need to manually upload files after every release. CI/CD pipelines should generate SBOMs and publish them automatically from trusted contexts. The platform then becomes part of the delivery feedback loop instead of an occasional compliance activity.</p><p>Dependency-Track is strongly aligned with CycloneDX. It consumes and produces CycloneDX SBOMs and supports CycloneDX VEX, which helps express whether a known vulnerability is exploitable, not affected, fixed, or otherwise understood in a specific product context. That distinction is critical because raw vulnerability counts are noisy. Useful vulnerability management depends on context, ownership, triage, and recorded decisions.</p><h2>5. What Dependency-Track is not</h2><p>Dependency-Track is not a silver bullet. It is not a replacement for secure build pipelines, dependency update discipline, lockfiles, package signing, provenance, secret scanning, runtime monitoring, code review, maintainership due diligence, or incident response. It improves visibility and coordination, but it does not remove the need for engineering judgment.</p><p>This boundary matters because a tool can create false confidence if it is misunderstood. Dependency-Track can show that a component exists in a project and that a vulnerability is associated with it. It cannot prove by itself that the vulnerable code path is reachable. It can raise a policy violation, but it cannot decide the organization&#8217;s risk appetite. It can record that a finding is not affected, but that statement is only useful if the analysis behind it is real.</p><p>Used well, Dependency-Track becomes part of how the organization learns and responds. Used badly, it becomes another dashboard that accumulates unresolved warnings.</p><h2>6. Core concepts</h2><p>A project represents a software product, application, service, library, container, firmware image, or other deliverable whose components you want to track. A project usually has a name and a version. The version can represent a release, an environment, a build number, a Git SHA, or another convention that matches how the organization needs to reason about impact.</p><p>A component is something the software is made of. That can include libraries, frameworks, application dependencies, operating system packages, containers, firmware, files, hardware, and services. Components may be identified through package URLs, CPEs, SWIDs, hashes, names, versions, licenses, and ecosystem-specific metadata.</p><p>An SBOM is the inventory uploaded to Dependency-Track. The platform is only as useful as the SBOMs it receives. An incomplete SBOM creates incomplete visibility. A stale SBOM creates historical evidence, not current operational knowledge. The practical goal is to generate an SBOM that represents what the team actually builds, packages, deploys, and operates.</p><p>A vulnerability is a known weakness associated with a component. Dependency-Track can correlate components with vulnerability intelligence sources and show where affected components exist across projects. The value is not only seeing that a CVE exists. The value is seeing how that CVE intersects with the organization&#8217;s software estate.</p><p>A policy is a rule that expresses what the organization considers acceptable or unacceptable. Dependency-Track supports security, license, and operational policy violations. A policy can raise a violation for a critical vulnerability, a forbidden license, an outdated component, an unauthorized package coordinate, or another condition that the organization wants to make visible.</p><p>An analysis state records the triage decision for a finding. Dependency-Track supports states such as exploitable, in triage, false positive, not affected, and not set. These states matter because real vulnerability management is not binary. A finding can be technically valid but not exploitable in the product&#8217;s context. Another finding can be a false positive due to incorrect component identification. Another one can be exploitable and urgent.</p><p>Suppression is used to hide or reduce noise from reviewed findings. It should be used carefully. A suppressed finding without evidence is hidden risk. A suppressed finding with context, owner, reason, and review expectations can be part of healthy triage.</p><h2>7. How the platform works</h2><p>The operating model is simple. Source code is built or packaged. The build process generates an SBOM. The SBOM is uploaded to Dependency-Track. The platform identifies components, correlates them with vulnerability intelligence and policy rules, and creates findings. Teams then triage those findings, remediate issues, suppress reviewed noise, or document why a project is not affected.</p><p>The important part is the loop. Dependency-Track should not be an end-of-quarter compliance exercise. It should be part of normal delivery. Every meaningful build or release should update the inventory. Every updated inventory should trigger analysis. Every important finding should create feedback. Every decision should leave history.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9kuI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbca20503-43f1-4cfe-8634-23a10e553e68_4664x262.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9kuI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbca20503-43f1-4cfe-8634-23a10e553e68_4664x262.png 424w, https://substackcdn.com/image/fetch/$s_!9kuI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbca20503-43f1-4cfe-8634-23a10e553e68_4664x262.png 848w, https://substackcdn.com/image/fetch/$s_!9kuI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbca20503-43f1-4cfe-8634-23a10e553e68_4664x262.png 1272w, https://substackcdn.com/image/fetch/$s_!9kuI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbca20503-43f1-4cfe-8634-23a10e553e68_4664x262.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9kuI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbca20503-43f1-4cfe-8634-23a10e553e68_4664x262.png" width="1456" height="82" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bca20503-43f1-4cfe-8634-23a10e553e68_4664x262.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:82,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:85896,&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://emmanuelvalverderamos.substack.com/i/197675797?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbca20503-43f1-4cfe-8634-23a10e553e68_4664x262.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_!9kuI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbca20503-43f1-4cfe-8634-23a10e553e68_4664x262.png 424w, https://substackcdn.com/image/fetch/$s_!9kuI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbca20503-43f1-4cfe-8634-23a10e553e68_4664x262.png 848w, https://substackcdn.com/image/fetch/$s_!9kuI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbca20503-43f1-4cfe-8634-23a10e553e68_4664x262.png 1272w, https://substackcdn.com/image/fetch/$s_!9kuI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbca20503-43f1-4cfe-8634-23a10e553e68_4664x262.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>8. Installation options</h2><p>Dependency-Track can be deployed in different ways depending on the level of maturity required. For local evaluation, Docker Compose is the fastest option. For production-like environments, Docker Compose with an external database may be enough for smaller installations. For Kubernetes environments, the official route is the Helm chart. More customized deployments can run the API server and frontend separately.</p><p>The platform has two main runtime parts: the API server and the frontend. This distinction matters because the frontend is static and the user&#8217;s browser must be able to reach the API server. A deployment that works inside the container network may still fail from the browser if the API base URL is not reachable externally.</p><p>For production, use an external database and treat the platform as security infrastructure. The Kubernetes documentation notes that the Helm chart default uses embedded H2, which is not intended for production usage.</p><h2>9. First local setup with Docker Compose</h2><p>For local evaluation, create a working directory and use the official Compose setup:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;02e54a9f-30cc-4bc5-8209-1168621afaab&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">mkdir dependency-track-lab
cd dependency-track-lab
curl -LO https://dependencytrack.org/docker-compose.yml
docker compose up -d
docker compose ps</code></pre></div><p>The frontend is commonly exposed on port <code>8080</code>, while the API server is commonly exposed on port <code>8081</code>. The API server is also where the OpenAPI documentation is exposed.</p><pre><code><code>Frontend: http://localhost:8080
API:      http://localhost:8081</code></code></pre><p>After creating an API key in the UI, set local environment variables:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;63868a27-7d30-4afb-a97d-0d1f139cf30c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">export DTRACK_URL="http://localhost:8081"
export DTRACK_API_KEY="replace-with-your-api-key"</code></pre></div><p>Then test connectivity:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;61e8db3e-83da-424a-a2d7-fb69a00ce2ab&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">curl -s \
  -H "X-Api-Key: ${DTRACK_API_KEY}" \
  "${DTRACK_URL}/api/version"</code></pre></div><p>A small script improves the developer experience because it makes the safe path repeatable:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;e1ba020d-b157-4916-84af-aa37b9063bdc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">#!/usr/bin/env bash
set -euo pipefail

: "${DTRACK_URL:?DTRACK_URL is required}"
: "${DTRACK_API_KEY:?DTRACK_API_KEY is required}"

curl -s \
  -H "X-Api-Key: ${DTRACK_API_KEY}" \
  "${DTRACK_URL}/api/version"</code></pre></div><h2>10. Production setup considerations</h2><p>A production Dependency-Track installation should have an external database, persistent storage, backups, TLS, authentication, authorization, restricted administration, API key rotation, monitoring, notification routing, and a clear upgrade process. It should also have conventions for project naming, versioning, ownership, tagging, stale project handling, and triage expectations.</p><p>This is not only operational housekeeping. Dependency-Track contains sensitive information about the organization&#8217;s software estate. A user with broad access can see internal projects, components, vulnerable dependencies, policy violations, and sometimes technology choices that reveal architecture and risk. Access should be granted intentionally.</p><p>A reasonable production checklist includes:</p><ul><li><p>External database</p></li><li><p>Persistent storage</p></li><li><p>Backups and restore tests</p></li><li><p>TLS</p></li><li><p>OIDC or LDAP integration</p></li><li><p>Least-privilege teams and roles</p></li><li><p>Separate API keys per automation context</p></li><li><p>API key rotation</p></li><li><p>Restricted administrator access</p></li><li><p>Monitoring for API server and worker health</p></li><li><p>Monitoring for BOM processing failures</p></li><li><p>Monitoring for feed synchronization failures</p></li><li><p>Notification routing</p></li><li><p>Upgrade process</p></li><li><p>Project ownership conventions</p></li><li><p>Stale project review process</p></li></ul><h2>11. Kubernetes deployment</h2><p>Dependency-Track can be installed on Kubernetes with Helm:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;ae03560b-10ae-4c90-8593-78fe655a9978&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">helm repo add dependency-track https://dependencytrack.github.io/helm-charts
helm repo update

helm install dtrack dependency-track/dependency-track \
  --namespace dtrack \
  --create-namespace</code></pre></div><p>This is a starting point, not a production design. For production, configure an external database, ingress, TLS, resource requests, resource limits, persistent storage, OIDC or LDAP integration, network policies, secret management, backups, logs, and metrics.</p><p>A useful deployment model looks like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3QK9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8440f0db-6b07-4526-8e6c-53208a891184_1679x606.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3QK9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8440f0db-6b07-4526-8e6c-53208a891184_1679x606.png 424w, https://substackcdn.com/image/fetch/$s_!3QK9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8440f0db-6b07-4526-8e6c-53208a891184_1679x606.png 848w, https://substackcdn.com/image/fetch/$s_!3QK9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8440f0db-6b07-4526-8e6c-53208a891184_1679x606.png 1272w, https://substackcdn.com/image/fetch/$s_!3QK9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8440f0db-6b07-4526-8e6c-53208a891184_1679x606.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3QK9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8440f0db-6b07-4526-8e6c-53208a891184_1679x606.png" width="1456" height="526" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8440f0db-6b07-4526-8e6c-53208a891184_1679x606.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:526,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:67391,&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://emmanuelvalverderamos.substack.com/i/197675797?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8440f0db-6b07-4526-8e6c-53208a891184_1679x606.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_!3QK9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8440f0db-6b07-4526-8e6c-53208a891184_1679x606.png 424w, https://substackcdn.com/image/fetch/$s_!3QK9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8440f0db-6b07-4526-8e6c-53208a891184_1679x606.png 848w, https://substackcdn.com/image/fetch/$s_!3QK9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8440f0db-6b07-4526-8e6c-53208a891184_1679x606.png 1272w, https://substackcdn.com/image/fetch/$s_!3QK9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8440f0db-6b07-4526-8e6c-53208a891184_1679x606.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>The browser must be able to reach the API server, not only the frontend. CI pipelines also need access to the API server to upload SBOMs.</p><h2>12. Creating projects</h2><p>Projects can be created manually, but CI automation is usually better. Dependency-Track supports uploading a BOM with <code>projectName</code>, <code>projectVersion</code>, and <code>autoCreate=true</code>, which allows the project to be created automatically if it does not exist.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;a20fba3d-c912-440c-ac5d-e9dca69003d1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">curl -sS -X POST "${DTRACK_URL}/api/v1/bom" \
  -H "X-Api-Key: ${DTRACK_API_KEY}" \
  -F "autoCreate=true" \
  -F "projectName=checkout-api" \
  -F "projectVersion=1.0.0" \
  -F "bom=@bom.json"</code></pre></div><p>Automatic creation is useful, but only if the organization has naming conventions. Otherwise, the portfolio can become polluted with inconsistent project names and versions. A practical convention is to use the repository or product name as the project name, the release version or deployment environment as the project version, and tags for team, domain, business capability, runtime, and criticality.</p><p>Good naming is not cosmetic. It improves incident response.</p><h2>13. Generating SBOMs</h2><p>Dependency-Track consumes SBOMs, so each ecosystem should generate CycloneDX using the most appropriate tool for that build. The organization does not need every team to use the same package manager or language. It needs every team to publish comparable evidence.</p><p>For Node.js:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;5094ffbd-56ee-44db-b4d5-37a28f5fd514&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">npx @cyclonedx/cyclonedx-npm --output-file bom.json</code></pre></div><p>For Maven:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;6964d900-2abd-4f25-8348-985b9718424e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom</code></pre></div><p>For Gradle:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;100cb6e0-ea8a-48da-898d-ca3c1fdb1a0a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">./gradlew cyclonedxBom</code></pre></div><p>For .NET:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;1492b3cb-1c7e-4b5c-be5e-bebbe4aeb73e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">dotnet CycloneDX ./src/MyProject.sln --output ./sbom</code></pre></div><p>For Python requirements:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;0a7a49b6-d9e9-4980-a072-8e3d8ec328bf&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">cyclonedx-py requirements requirements.txt -o bom.json</code></pre></div><p>For container images with Syft:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;f728997b-a442-4336-bd59-7060b58a9cc2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">syft my-image:latest -o cyclonedx-json=bom.json</code></pre></div><p>The key rule is to generate the SBOM as close as possible to the artifact you actually ship. Source-level SBOMs are useful, but final container image SBOMs can reveal operating system packages and image-layer dependencies that source-level generation may miss.</p><h2>14. Uploading SBOMs</h2><p>The most common upload approach is multipart form data through the API:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;7309df5d-be01-472a-b5f7-2a2fea71397d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">curl -sS -X POST "${DTRACK_URL}/api/v1/bom" \
  -H "X-Api-Key: ${DTRACK_API_KEY}" \
  -F "autoCreate=true" \
  -F "projectName=${PROJECT_NAME}" \
  -F "projectVersion=${PROJECT_VERSION}" \
  -F "bom=@${BOM_FILE}"</code></pre></div><p>A more defensive script is better for team usage:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;f3ea6e79-9f42-4564-bd19-34e3b1e1a8f3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">#!/usr/bin/env bash
set -euo pipefail

: "${DTRACK_URL:?DTRACK_URL is required}"
: "${DTRACK_API_KEY:?DTRACK_API_KEY is required}"
: "${PROJECT_NAME:?PROJECT_NAME is required}"
: "${PROJECT_VERSION:?PROJECT_VERSION is required}"
: "${BOM_FILE:?BOM_FILE is required}"

if [[ ! -f "${BOM_FILE}" ]]; then
  echo "SBOM file not found: ${BOM_FILE}" &gt;&amp;2
  exit 1
fi

curl -sS -X POST "${DTRACK_URL}/api/v1/bom" \
  -H "X-Api-Key: ${DTRACK_API_KEY}" \
  -F "autoCreate=true" \
  -F "projectName=${PROJECT_NAME}" \
  -F "projectVersion=${PROJECT_VERSION}" \
  -F "bom=@${BOM_FILE}"</code></pre></div><p>This kind of small automation matters for DevEx. It reduces friction, avoids copy-and-paste errors, and gives teams a repeatable path.</p><h2>15. CI/CD integration</h2><p>The CI/CD integration should be boring. A good pipeline does not ask developers to remember Dependency-Track. It generates the SBOM and uploads it automatically from a trusted context.</p><p>A practical flow is:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GQAZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae78f83-5ad9-4323-b857-747c07df8a93_3568x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GQAZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae78f83-5ad9-4323-b857-747c07df8a93_3568x140.png 424w, https://substackcdn.com/image/fetch/$s_!GQAZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae78f83-5ad9-4323-b857-747c07df8a93_3568x140.png 848w, https://substackcdn.com/image/fetch/$s_!GQAZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae78f83-5ad9-4323-b857-747c07df8a93_3568x140.png 1272w, https://substackcdn.com/image/fetch/$s_!GQAZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae78f83-5ad9-4323-b857-747c07df8a93_3568x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GQAZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae78f83-5ad9-4323-b857-747c07df8a93_3568x140.png" width="1456" height="57" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4ae78f83-5ad9-4323-b857-747c07df8a93_3568x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:57,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:38665,&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://emmanuelvalverderamos.substack.com/i/197675797?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae78f83-5ad9-4323-b857-747c07df8a93_3568x140.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_!GQAZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae78f83-5ad9-4323-b857-747c07df8a93_3568x140.png 424w, https://substackcdn.com/image/fetch/$s_!GQAZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae78f83-5ad9-4323-b857-747c07df8a93_3568x140.png 848w, https://substackcdn.com/image/fetch/$s_!GQAZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae78f83-5ad9-4323-b857-747c07df8a93_3568x140.png 1272w, https://substackcdn.com/image/fetch/$s_!GQAZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae78f83-5ad9-4323-b857-747c07df8a93_3568x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The order matters. If the SBOM is generated before dependency resolution, it may not represent the real graph. If it is generated before packaging, it may miss runtime components. If SBOMs are generated manually from developer machines, the organization loses consistency.</p><p>For higher maturity, teams can generate separate SBOMs for application dependencies, container images, internal libraries, and released artifacts. That does not need to happen on day one, but the model should be understood from the beginning.</p><h2>16. GitHub Actions example</h2><p>A simple Node.js workflow can generate and upload an SBOM:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;b7ac0910-c52b-475c-991c-04b1c5b05f49&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">name: sbom

on:
  push:
    branches:
      - main
  workflow_dispatch:

permissions:
  contents: read

jobs:
  generate-and-upload-sbom:
    runs-on: ubuntu-latest

    env:
      PROJECT_NAME: checkout-api
      PROJECT_VERSION: main
      BOM_FILE: bom.json

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Generate CycloneDX SBOM
        run: npx @cyclonedx/cyclonedx-npm --output-file "${BOM_FILE}"

      - name: Upload SBOM to Dependency-Track
        env:
          DTRACK_URL: ${{ secrets.DTRACK_URL }}
          DTRACK_API_KEY: ${{ secrets.DTRACK_API_KEY }}
        run: |
          set -euo pipefail

          curl -sS -X POST "${DTRACK_URL}/api/v1/bom" \
            -H "X-Api-Key: ${DTRACK_API_KEY}" \
            -F "autoCreate=true" \
            -F "projectName=${PROJECT_NAME}" \
            -F "projectVersion=${PROJECT_VERSION}" \
            -F "bom=@${BOM_FILE}"</code></pre></div><p>For production usage, the workflow should be tightened. Keep permissions minimal, avoid exposing API keys to forked pull requests, use trusted branches or release workflows for uploads, rotate secrets, and review any workflow pattern that gives untrusted code access to privileged tokens. The TanStack incident makes this especially important because CI security is part of supply-chain security.</p><h2>17. Jenkins approach</h2><p>Dependency-Track has a Jenkins plugin, and Jenkins environments can also upload SBOMs directly through the API. The pipeline shape is similar: checkout, install, test, build, generate SBOM, publish SBOM.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;61241c19-8e0d-4d02-b40a-a30bc336726c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">pipeline {
  agent any

  environment {
    PROJECT_NAME = 'checkout-api'
    PROJECT_VERSION = "${env.BUILD_NUMBER}"
    BOM_FILE = 'bom.json'
  }

  stages {
    stage('Install') {
      steps {
        sh 'npm ci'
      }
    }

    stage('Test') {
      steps {
        sh 'npm test'
      }
    }

    stage('Generate SBOM') {
      steps {
        sh 'npx @cyclonedx/cyclonedx-npm --output-file bom.json'
      }
    }

    stage('Upload SBOM') {
      steps {
        withCredentials([
          string(credentialsId: 'dependency-track-api-key', variable: 'DTRACK_API_KEY'),
          string(credentialsId: 'dependency-track-url', variable: 'DTRACK_URL')
        ]) {
          sh '''
            set -euo pipefail

            curl -sS -X POST "${DTRACK_URL}/api/v1/bom" \
              -H "X-Api-Key: ${DTRACK_API_KEY}" \
              -F "autoCreate=true" \
              -F "projectName=${PROJECT_NAME}" \
              -F "projectVersion=${PROJECT_VERSION}" \
              -F "bom=@${BOM_FILE}"
          '''
        }
      }
    }
  }
}</code></pre></div><p>The same security rule applies. The Dependency-Track API key should be available only in trusted jobs that need to upload SBOMs.</p><h2>18. Working with multiple ecosystems</h2><p>Dependency-Track is useful because it is not tied to one ecosystem. A real product may include a React frontend, a Java backend, a Python worker, a .NET internal service, a container base image, Terraform modules, GitHub Actions, and shared internal packages. A single language-specific scanner cannot represent that whole system well.</p><p>The better model is to standardize the evidence format, not the entire engineering stack. Each ecosystem can generate CycloneDX in the way that fits its build. Dependency-Track then provides the shared analysis layer.</p><p>This is especially important for DevEx. Developers keep using the tools that make sense for their context, while the organization gets consistent visibility.</p><h2>19. Improving DevEx with Taskfile</h2><p>Dependency-Track becomes much more valuable when developers do not need to remember how to use it. The worst version of an SBOM workflow is a README section full of long commands that every team copies, modifies, forgets, and eventually breaks. The better version is a small set of repeatable tasks that work locally and in CI with the same interface.</p><p>This is where Taskfile fits naturally. Task gives the project a cross-platform command layer that can hide incidental complexity without hiding the important decisions. A developer should not need to remember the exact CycloneDX command, the Dependency-Track endpoint, the required form fields, or the right curl flags. The repository can expose a few intention-revealing commands instead:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;83234e6d-bb3a-46d7-896a-9a3ac9a05b85&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">task sbom:generate
task sbom:upload
task sbom:publish
task dtrack:health</code></pre></div><p>That changes the developer experience. Instead of asking every team to become familiar with Dependency-Track&#8217;s API, the project gives them a paved road. The details still exist, but they live in one place. The workflow becomes easier to run locally, easier to reuse in CI, easier to document, and easier to evolve when the organization changes its conventions.</p><p>A simple Taskfile can start like this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;1c0a6b78-165a-4db9-9047-382fdab0e9af&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  PROJECT_NAME:
    sh: basename "$(pwd)"
  PROJECT_VERSION:
    sh: git rev-parse --short HEAD
  BOM_FILE: bom.json

tasks:
  dtrack:health:
    desc: Check that Dependency-Track API is reachable
    cmds:
      - |
        set -euo pipefail

        : "${DTRACK_URL:?DTRACK_URL is required}"
        : "${DTRACK_API_KEY:?DTRACK_API_KEY is required}"

        curl -sS \
          -H "X-Api-Key: ${DTRACK_API_KEY}" \
          "${DTRACK_URL}/api/version"

  sbom:generate:
    desc: Generate a CycloneDX SBOM for this project
    cmds:
      - npx @cyclonedx/cyclonedx-npm --output-file "{{.BOM_FILE}}"
    sources:
      - package.json
      - package-lock.json
    generates:
      - '{{.BOM_FILE}}'

  sbom:upload:
    desc: Upload the generated SBOM to Dependency-Track
    deps:
      - sbom:generate
    cmds:
      - |
        set -euo pipefail

        : "${DTRACK_URL:?DTRACK_URL is required}"
        : "${DTRACK_API_KEY:?DTRACK_API_KEY is required}"

        test -f "{{.BOM_FILE}}"

        curl -sS -X POST "${DTRACK_URL}/api/v1/bom" \
          -H "X-Api-Key: ${DTRACK_API_KEY}" \
          -F "autoCreate=true" \
          -F "projectName={{.PROJECT_NAME}}" \
          -F "projectVersion={{.PROJECT_VERSION}}" \
          -F "bom=@{{.BOM_FILE}}"

  sbom:publish:
    desc: Generate and upload the SBOM
    cmds:
      - task: sbom:upload</code></pre></div><p>This is already better than a loose script because the tasks communicate intent. <code>sbom:generate</code> creates the evidence. <code>sbom:upload</code> sends it to Dependency-Track. <code>sbom:publish</code> gives CI and developers one stable entry point. If the implementation changes later, the interface can stay the same.</p><p>For local development, environment variables can be loaded from a <code>.env</code> file, while CI can inject them through its secret store. That keeps secrets out of the repository and avoids forcing developers to paste API keys into commands.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;cea5dd8e-7bfb-4d48-93fe-5768efbb6235&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

dotenv:
  - .env
  - '{{.HOME}}/.config/dependency-track/.env'

vars:
  PROJECT_NAME:
    sh: basename "$(pwd)"
  PROJECT_VERSION:
    sh: git rev-parse --short HEAD
  BOM_FILE: bom.json</code></pre></div><p>A local <code>.env</code> file can look like this:</p><pre><code><code>DTRACK_URL=http://localhost:8081
DTRACK_API_KEY=replace-with-your-local-api-key</code></code></pre><p>The <code>.env</code> file should be ignored by Git:</p><pre><code><code>.env
bom.json</code></code></pre><p>This small detail matters. Good DevEx does not ask people to choose between convenience and safety. It gives them a convenient path that is also safe by default.</p><p>Taskfile also helps when the organization supports multiple ecosystems. A frontend, backend, worker, and container image may need different SBOM generation commands, but the public interface can stay consistent.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;eb1f46bd-75cc-43ec-a9f1-04393ee636f3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  sbom:node:
    desc: Generate SBOM for a Node.js project
    cmds:
      - npx @cyclonedx/cyclonedx-npm --output-file bom-node.json
    sources:
      - package.json
      - package-lock.json
    generates:
      - bom-node.json

  sbom:maven:
    desc: Generate SBOM for a Maven project
    cmds:
      - mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom
    sources:
      - pom.xml

  sbom:container:
    desc: Generate SBOM for a container image
    vars:
      IMAGE: '{{.IMAGE | default "checkout-api:local"}}'
      BOM_FILE: bom-container.json
    cmds:
      - syft "{{.IMAGE}}" -o cyclonedx-json="{{.BOM_FILE}}"
    generates:
      - bom-container.json

  sbom:all:
    desc: Generate all SBOMs for this repository
    cmds:
      - task: sbom:node
      - task: sbom:container</code></pre></div><p>This keeps ecosystem-specific complexity inside the repository while preserving a shared organizational pattern. Teams can adapt the internal command for their stack without changing the mental model. The frontend team, backend team, and platform team can all say &#8220;run the SBOM task&#8221; and mean the same kind of workflow.</p><p>For CI/CD, Taskfile can reduce duplication. Instead of writing the full SBOM logic inside GitHub Actions, GitLab CI, Jenkins, or Azure Pipelines, the pipeline can call the same task developers use locally.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;def0d2be-1ca3-43a9-8670-e7ffdb801f7c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">name: sbom

on:
  push:
    branches:
      - main
  workflow_dispatch:

permissions:
  contents: read

jobs:
  publish-sbom:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Task
        uses: arduino/setup-task@v2

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Publish SBOM
        env:
          DTRACK_URL: ${{ secrets.DTRACK_URL }}
          DTRACK_API_KEY: ${{ secrets.DTRACK_API_KEY }}
        run: task sbom:publish</code></pre></div><p>This is a better maintenance model. The CI file describes the delivery flow, while the Taskfile owns the project workflow. When the SBOM generator changes, the team changes the Taskfile once. When the Dependency-Track upload convention changes, the team changes the Taskfile once. When a developer wants to reproduce the CI behavior locally, they run the same command.</p><p>Taskfile can also make safety checks explicit. Before uploading an SBOM, the task can verify that required environment variables exist, that the SBOM file was generated, that the Dependency-Track API is reachable, and that the upload only happens from an expected branch or trusted CI context.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;5732de06-28f7-4178-990e-f8bbb26cacc1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  sbom:check-env:
    desc: Validate required Dependency-Track environment variables
    cmds:
      - |
        set -euo pipefail

        : "${DTRACK_URL:?DTRACK_URL is required}"
        : "${DTRACK_API_KEY:?DTRACK_API_KEY is required}"

  sbom:check-file:
    desc: Validate that the SBOM file exists
    vars:
      BOM_FILE: '{{.BOM_FILE | default "bom.json"}}'
    cmds:
      - test -s "{{.BOM_FILE}}"

  sbom:upload:
    desc: Upload SBOM to Dependency-Track
    deps:
      - sbom:check-env
      - sbom:generate
      - sbom:check-file
    cmds:
      - |
        set -euo pipefail

        curl -sS -X POST "${DTRACK_URL}/api/v1/bom" \
          -H "X-Api-Key: ${DTRACK_API_KEY}" \
          -F "autoCreate=true" \
          -F "projectName={{.PROJECT_NAME}}" \
          -F "projectVersion={{.PROJECT_VERSION}}" \
          -F "bom=@{{.BOM_FILE}}"</code></pre></div><p>This is a small example, but the design principle is important. A good DevEx workflow should fail early, fail clearly, and fail before doing something risky. Missing secrets, missing SBOMs, wrong project names, and unreachable APIs should be discovered by the task, not by a developer reading a vague CI failure five minutes later.</p><p>For larger organizations, split Taskfiles can keep the workflow maintainable. A root <code>Taskfile.yml</code> can include a shared Dependency-Track task file. Each repository can then keep a small local file while reusing the same organizational conventions.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;7db19073-299b-4e34-b1a0-825951548576&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

includes:
  dtrack:
    taskfile: ./tasks/Taskfile.dtrack.yml

tasks:
  sbom:publish:
    desc: Publish this project SBOM to Dependency-Track
    cmds:
      - task: dtrack:publish</code></pre></div><p>The included file can contain the standard implementation:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;bb53c04e-1196-4f54-9e2c-68bb2030ad8f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  PROJECT_NAME:
    sh: basename "$(pwd)"
  PROJECT_VERSION:
    sh: git rev-parse --short HEAD
  BOM_FILE: bom.json

tasks:
  generate:
    desc: Generate CycloneDX SBOM
    cmds:
      - npx @cyclonedx/cyclonedx-npm --output-file "{{.BOM_FILE}}"

  publish:
    desc: Generate and upload SBOM to Dependency-Track
    deps:
      - generate
    cmds:
      - |
        set -euo pipefail

        : "${DTRACK_URL:?DTRACK_URL is required}"
        : "${DTRACK_API_KEY:?DTRACK_API_KEY is required}"

        curl -sS -X POST "${DTRACK_URL}/api/v1/bom" \
          -H "X-Api-Key: ${DTRACK_API_KEY}" \
          -F "autoCreate=true" \
          -F "projectName={{.PROJECT_NAME}}" \
          -F "projectVersion={{.PROJECT_VERSION}}" \
          -F "bom=@{{.BOM_FILE}}"</code></pre></div><p>This pattern is especially useful when Dependency-Track adoption moves beyond one team. The organization can provide a recommended task module, while each repository can override variables such as project name, version, image name, or SBOM generator. That gives teams flexibility without losing consistency.</p><p>A mature setup can go further and use Taskfile as the local command surface for the whole supply-chain workflow:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;d7ce3277-051a-4f11-b48d-2bcf296c421a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">task dtrack:up
task dtrack:health
task sbom:generate
task sbom:validate
task sbom:upload
task sbom:publish
task sbom:container
task sbom:clean</code></pre></div><p>For example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;f32cfd79-60c5-4973-bd93-1a6d7c108e10&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  dtrack:up:
    desc: Start local Dependency-Track for experimentation
    cmds:
      - docker compose -f docker-compose.dependency-track.yml up -d

  dtrack:down:
    desc: Stop local Dependency-Track
    cmds:
      - docker compose -f docker-compose.dependency-track.yml down

  sbom:clean:
    desc: Remove generated SBOM files
    cmds:
      - rm -f bom.json bom-node.json bom-container.json

  sbom:validate:
    desc: Validate that the generated SBOM exists and is valid JSON
    vars:
      BOM_FILE: '{{.BOM_FILE | default "bom.json"}}'
    cmds:
      - test -s "{{.BOM_FILE}}"
      - jq empty "{{.BOM_FILE}}"

  sbom:publish:
    desc: Generate, validate, and upload SBOM
    cmds:
      - task: sbom:generate
      - task: sbom:validate
      - task: sbom:upload</code></pre></div><p>The point is not to create a beautiful Taskfile for its own sake. The point is to reduce the cost of doing the right thing. A workflow that can be run with one command is easier to adopt, easier to teach, easier to automate, and easier to debug.</p><p>This also improves onboarding. A new developer does not need to read the whole Dependency-Track guide before contributing. They can run:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;ad8b323a-ef18-44cc-ae89-fa39cbe332a4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">task --list</code></pre></div><p>and see the available workflows. The Taskfile becomes executable documentation. It shows what the project expects, how SBOMs are generated, how they are uploaded, and which commands are safe to run locally.</p><p>The economic value is clear. Taskfile reduces coordination cost because teams share a common command vocabulary. It reduces cognitive load because developers do not need to remember long commands. It reduces operational variation because CI and local workflows can call the same tasks. It reduces incident response cost because SBOM generation and upload are already part of the normal workflow before an incident happens.</p><p>That is the right relationship between Dependency-Track and Taskfile. Dependency-Track provides the organizational memory of software composition. Taskfile provides the repeatable developer interface that keeps that memory fresh. One gives visibility. The other makes the workflow easy enough that teams actually use it.</p><h2>20. Policies that actually help teams</h2><p>Policies should create clarity, not noise. A useful policy is clear, actionable, explainable, owned, reviewable, and proportional to risk. A bad policy turns Dependency-Track into a noisy compliance machine that teams learn to ignore.</p><p>Good initial policies are simple. For security, a team might raise a violation when a production project has a critical vulnerability. For license governance, a team might flag forbidden licenses in customer-distributed software. For operational risk, a team might identify prohibited package coordinates or components that are too old compared with the latest known version.</p><p>A healthy rollout usually starts with visibility before enforcement. First observe findings, understand noise, define ownership, and teach triage. Then introduce policies and notifications. Only after the organization can respond reliably should selected gates be added to delivery workflows.</p><p>A policy that nobody can act on is not a control. It is just inventory with a threatening label.</p><h2>21. Triage workflow</h2><p>Triage is where Dependency-Track becomes useful. A finding without triage is only an alarm. A triaged finding becomes organizational knowledge.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8_tP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdb4baa-3334-4b21-87c3-944501ceed94_2129x3180.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8_tP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdb4baa-3334-4b21-87c3-944501ceed94_2129x3180.png 424w, https://substackcdn.com/image/fetch/$s_!8_tP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdb4baa-3334-4b21-87c3-944501ceed94_2129x3180.png 848w, https://substackcdn.com/image/fetch/$s_!8_tP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdb4baa-3334-4b21-87c3-944501ceed94_2129x3180.png 1272w, https://substackcdn.com/image/fetch/$s_!8_tP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdb4baa-3334-4b21-87c3-944501ceed94_2129x3180.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8_tP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdb4baa-3334-4b21-87c3-944501ceed94_2129x3180.png" width="1456" height="2175" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/efdb4baa-3334-4b21-87c3-944501ceed94_2129x3180.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2175,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:281678,&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://emmanuelvalverderamos.substack.com/i/197675797?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdb4baa-3334-4b21-87c3-944501ceed94_2129x3180.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_!8_tP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdb4baa-3334-4b21-87c3-944501ceed94_2129x3180.png 424w, https://substackcdn.com/image/fetch/$s_!8_tP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdb4baa-3334-4b21-87c3-944501ceed94_2129x3180.png 848w, https://substackcdn.com/image/fetch/$s_!8_tP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdb4baa-3334-4b21-87c3-944501ceed94_2129x3180.png 1272w, https://substackcdn.com/image/fetch/$s_!8_tP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdb4baa-3334-4b21-87c3-944501ceed94_2129x3180.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The analysis states should be used honestly. <code>False positive</code> should mean the finding is wrong. <code>Not affected</code> should mean the finding is real but does not affect the project in its context. <code>In triage</code> should mean someone is actively investigating. <code>Exploitable</code> should mean the issue is exploitable or likely exploitable in this project. <code>Not set</code> should not become the place where findings go to be forgotten.</p><p>The quality of triage determines the quality of the system.</p><h2>22. Notifications</h2><p>Dependency-Track supports notifications for system and portfolio events. Notifications can be routed to collaboration tools, email, webhooks, Jira, and other integrations depending on the setup.</p><p>Notification design should be intentional. Sending everything to everyone creates alert fatigue. A better approach is to route different events to different audiences. Platform teams may need BOM processing failures. Security teams may need critical vulnerability summaries. Product teams may need policy violations for their own projects. Compliance teams may need license-related summaries. Automation may need webhook payloads for ticket creation or reporting.</p><p>Tickets should be created only when ownership and action are clear. Every unnecessary ticket becomes extra work in the system. The goal is not to maximize activity. The goal is to create timely, relevant feedback.</p><h2>23. Dashboards and portfolio visibility</h2><p>The dashboard is not the outcome. The decisions made from the dashboard are the outcome. Dependency-Track can show risk across projects, components, vulnerabilities, policies, and portfolio trends, but the organization must decide which questions matter.</p><p>Useful questions include which projects have unresolved critical findings, which teams have untriaged issues, which vulnerable component creates the largest blast radius, which policies generate the most noise, which findings are suppressed without enough context, and which projects have stopped uploading SBOMs.</p><p>Total vulnerability count is usually a weak metric. Better metrics combine severity, exploitability, exposure, ownership, business criticality, and freshness. A critical finding in an internet-facing production system with no owner is different from a low-severity finding in an internal prototype that is being deleted next week.</p><p>Metrics should improve behavior. If they only create shame, they will be gamed or ignored.</p><h2>24. REST API and automation</h2><p>Dependency-Track exposes a REST API and OpenAPI documentation, which makes it suitable for automation. Common uses include uploading SBOMs from CI, creating projects automatically, applying tags, exporting findings, checking BOM processing status, creating selected tickets, feeding risk data into internal developer portals, and integrating with Backstage or similar platforms.</p><p>A simple project query looks like this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;9b58efb5-5652-4299-ad51-92f32deb9004&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">curl -s \
  -H "X-Api-Key: ${DTRACK_API_KEY}" \
  "${DTRACK_URL}/api/v1/project"</code></pre></div><p>With <code>jq</code>, the output becomes easier to consume:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;e37316fe-b8c3-4184-9b3f-fccb32c18461&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">curl -s \
  -H "X-Api-Key: ${DTRACK_API_KEY}" \
  "${DTRACK_URL}/api/v1/project" \
  | jq '.[] | {name, version, uuid}'</code></pre></div><p>A lookup by project name and version can be scripted:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;097c444c-3de2-4b54-ba06-6214cb46e4cd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">curl -s \
  -H "X-Api-Key: ${DTRACK_API_KEY}" \
  "${DTRACK_URL}/api/v1/project/lookup?name=${PROJECT_NAME}&amp;version=${PROJECT_VERSION}" \
  | jq '{name, version, uuid}'</code></pre></div><p>Automation should use explicit inputs and CI secrets. API keys should not be committed, echoed, or shared across unrelated workflows.</p><h2>25. Security patterns around Dependency-Track</h2><p>Dependency-Track should be secured like any other security-sensitive platform. Use OIDC or LDAP where possible, avoid shared admin accounts, apply least privilege, rotate API keys, use separate API keys per automation context, store secrets in CI secret stores, restrict network access, protect the platform with TLS, back up the database, and monitor BOM processing failures.</p><p>The data inside Dependency-Track is sensitive because it describes the internal software estate. It can reveal technologies, vulnerabilities, ownership, architecture hints, and risk concentration. That information is valuable for defenders, but it can also be useful for attackers.</p><p>The same security thinking should apply to the automation around the platform. A Dependency-Track upload key should not be available to untrusted pull request code. It should not be stored in a repository-level <code>.env</code> file that can be accidentally committed. It should not be reused across all projects if separate keys would give better control. The tool is there to improve supply-chain visibility, but its own access model must be designed carefully.</p><h2>26. How to use it during an incident</h2><p>Imagine that a malicious version of a package has been published. The first question is whether the organization uses the affected package and version. Without a central inventory, that question becomes a manual search across teams. With Dependency-Track, the response can start from the component and move outward to projects, owners, environments, and remediation status.</p><p>A practical incident flow is to identify the affected package coordinates and versions, search for them across the portfolio, identify affected projects, contact owners, prioritize production and exposed systems, check when the affected versions entered the build, rotate credentials where install or build hosts may have been exposed, remediate or isolate affected systems, upload new SBOMs, and verify that the affected components no longer appear.</p><p>Dependency-Track does not perform the whole incident response. It provides the map. During an incident, that map is leverage.</p><h2>27. How it helps with supply-chain attacks</h2><p>Different supply-chain attacks work differently, but they often share the same economic pattern: the attacker compromises something that many others trust, and the downstream cost is pushed to everyone who depends on it.</p><p>In a TanStack-style npm incident, Dependency-Track helps identify projects that include affected package versions after SBOMs have been uploaded. It does not secure CI caches, OIDC tokens, or publishing workflows. Those require CI hardening and release governance.</p><p>In an XZ Utils-style incident, Dependency-Track can help identify affected versions if SBOMs include operating system and container image components. If the organization only tracks application package dependencies, this class of exposure may be missed.</p><p>In a SolarWinds-style vendor compromise, Dependency-Track can contribute to component and service visibility, but vendor risk also requires procurement controls, asset inventory, network monitoring, and incident response.</p><p>In a Codecov-style CI tooling compromise, Dependency-Track can help track software components, but the organization also needs to treat build tools, uploaders, scripts, and CI dependencies as part of the supply chain.</p><p>The dependency graph is bigger than the codebase. The visibility model must be bigger too.</p><h2>28. Common mistakes</h2><p>Common adoption mistakes include treating SBOM generation as a checkbox, uploading once and forgetting it, creating project versions without a strategy, failing to define ownership, suppressing findings without evidence, turning every finding into a ticket, blocking builds before teams can respond, exposing API keys to untrusted CI contexts, ignoring container and operating system packages, and confusing vulnerability count with actual risk.</p><p>Most of these mistakes have the same root cause: the organization treats Dependency-Track as a tool to install rather than a feedback loop to design. The installation is the easy part. The value comes from the operating model around it.</p><p>A particularly common mistake is adding Dependency-Track as a security requirement without improving the developer workflow. If developers have to remember long commands, manually upload files, guess project names, search for secrets, and interpret every finding alone, the organization has added friction rather than capability. Taskfile helps because it turns that friction into a small set of project commands that can be run locally and reused in CI.</p><h2>29. Adoption roadmap</h2><p>Start with a local evaluation. Run Dependency-Track with Docker Compose, generate one SBOM from one real project, upload it, review the components, inspect the findings, and understand the difference between vulnerabilities, policy violations, analysis states, and suppressions.</p><p>Then add a Taskfile. Create <code>task dtrack:health</code>, <code>task sbom:generate</code>, <code>task sbom:validate</code>, and <code>task sbom:upload</code>. This step matters because it turns an experiment into a repeatable workflow. The team should be able to run the same command tomorrow without rereading the setup notes.</p><p>After that, add CI integration. The main branch or release workflow should generate and upload SBOMs automatically from a trusted context. API keys should be stored in CI secrets and should not be available to untrusted pull request code. The pipeline can call <code>task sbom:publish</code> so local and CI behavior stay aligned.</p><p>Then define portfolio conventions. Project naming, versioning, owner tags, domain tags, criticality tags, and freshness expectations are essential if the organization wants useful incident response.</p><p>After that, introduce triage discipline. Teams need to understand when to use exploitable, not affected, false positive, in triage, and not set. Suppressions should include context, and critical findings should not remain undecided.</p><p>Only then should policies become stronger. Start with reporting, reduce noise, route notifications correctly, and introduce gates only for risks the organization is ready to act on.</p><p>Finally, practice incident response. Pick a known package, search for it across the portfolio, identify owners, simulate remediation, upload a new SBOM, and verify the result. This practice is where the tool becomes real.</p><h2>30. Conclusions</h2><p>Dependency-Track is valuable because it changes the shape of dependency management. It moves teams from local scanning to portfolio visibility, from static SBOM files to living operational evidence, from noisy vulnerability alerts to recorded triage decisions, and from manual dependency archaeology to faster impact analysis.</p><p>That matters because supply-chain attacks are not rare edge cases anymore. TanStack, XZ Utils, SolarWinds, Codecov, and other incidents show different versions of the same problem: trusted software, trusted automation, trusted packages, trusted vendors, and trusted build steps can become paths of compromise.</p><p>Dependency-Track does not remove that risk. It reduces blindness. In software economics terms, it creates option value because it gives the organization a faster way to respond when new information arrives. It lowers discovery cost, reduces coordination cost, and improves the liquidity of security decisions because teams can redirect attention toward affected systems instead of first trying to find them.</p><p>Taskfile strengthens that value because it makes the workflow easier to repeat. A platform that depends on manual discipline will decay. A platform connected to simple local commands and CI tasks has a better chance of staying fresh. Developers get a clear command surface. CI gets a reusable implementation. Security gets better evidence. The organization gets a healthier feedback loop.</p><p>The promise is not perfect security. The promise is better visibility, faster learning, clearer decisions, lower friction, and safer delivery.</p><h2>References</h2><ul><li><p>OWASP Foundation. <em>Dependency-Track documentation</em>. </p></li></ul><p>https://docs.dependencytrack.org/</p><ul><li><p>OWASP Foundation. <em>Dependency-Track REST API</em>. <a href="https://docs.dependencytrack.org/integrations/rest-api/">https://docs.dependencytrack.org/integrations/rest-api/</a></p></li><li><p>OWASP Foundation. <em>Dependency-Track Continuous Integration &amp; Delivery</em>. <a href="https://docs.dependencytrack.org/usage/cicd/">https://docs.dependencytrack.org/usage/cicd/</a></p></li><li><p>OWASP Foundation. <em>Dependency-Track Policy Compliance</em>. <a href="https://docs.dependencytrack.org/usage/policy-compliance/">https://docs.dependencytrack.org/usage/policy-compliance/</a></p></li><li><p>OWASP Foundation. <em>Dependency-Track Auditing Basics</em>. <a href="https://docs.dependencytrack.org/triage/auditing-basics/">https://docs.dependencytrack.org/triage/auditing-basics/</a></p></li><li><p>OWASP Foundation. <em>Dependency-Track Analysis States</em>. <a href="https://docs.dependencytrack.org/triage/analysis-states/">https://docs.dependencytrack.org/triage/analysis-states/</a></p></li><li><p>OWASP Foundation. <em>Dependency-Track Notifications</em>. <a href="https://docs.dependencytrack.org/integrations/notifications/">https://docs.dependencytrack.org/integrations/notifications/</a></p></li><li><p>OWASP Foundation. <em>Dependency-Track Deploying Docker Container</em>. <a href="https://docs.dependencytrack.org/getting-started/deploy-docker/">https://docs.dependencytrack.org/getting-started/deploy-docker/</a></p></li><li><p>OWASP Foundation. <em>Dependency-Track Deploying on Kubernetes</em>. <a href="https://docs.dependencytrack.org/getting-started/deploy-kubernetes/">https://docs.dependencytrack.org/getting-started/deploy-kubernetes/</a></p></li><li><p>OWASP Foundation. <em>Dependency-Track Configuration</em>. <a href="https://docs.dependencytrack.org/getting-started/configuration/">https://docs.dependencytrack.org/getting-started/configuration/</a></p></li><li><p>OWASP Foundation. <em>Dependency-Track Change Log</em>. <a href="https://docs.dependencytrack.org/changelog/">https://docs.dependencytrack.org/changelog/</a></p></li><li><p>Task. <em>Guide</em>. <a href="https://taskfile.dev/docs/guide">https://taskfile.dev/docs/guide</a></p></li><li><p>Task. <em>Taskfile Schema Reference</em>. <a href="https://taskfile.dev/docs/reference/schema">https://taskfile.dev/docs/reference/schema</a></p></li><li><p>Task. <em>Environment Reference</em>. <a href="https://taskfile.dev/docs/reference/environment">https://taskfile.dev/docs/reference/environment</a></p></li><li><p>Task. <em>Taskfile Versions</em>. <a href="https://taskfile.dev/docs/taskfile-versions">https://taskfile.dev/docs/taskfile-versions</a></p></li><li><p>Linsley, Tanner. <em>Postmortem: TanStack npm supply-chain compromise</em>. <a href="https://tanstack.com/blog/npm-supply-chain-compromise-postmortem">https://tanstack.com/blog/npm-supply-chain-compromise-postmortem</a></p></li><li><p>GitHub Security Lab. <em>Keeping your GitHub Actions and workflows secure: Preventing pwn requests</em>. <a href="https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/">https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/</a></p></li><li><p>Cybersecurity and Infrastructure Security Agency. <em>Reported Supply Chain Compromise Affecting XZ Utils Data Compression Library, CVE-2024-3094</em>. <a href="https://www.cisa.gov/news-events/alerts/2024/03/29/reported-supply-chain-compromise-affecting-xz-utils-data-compression-library-cve-2024-3094">https://www.cisa.gov/news-events/alerts/2024/03/29/reported-supply-chain-compromise-affecting-xz-utils-data-compression-library-cve-2024-3094</a></p></li><li><p>National Institute of Standards and Technology. <em>CVE-2024-3094 Detail</em>. <a href="https://nvd.nist.gov/vuln/detail/cve-2024-3094">https://nvd.nist.gov/vuln/detail/cve-2024-3094</a></p></li><li><p>SolarWinds. <em>Security Advisory RE: CERT Emergency Directive</em>. <a href="https://www.solarwinds.com/sa-overview/certadvisory">https://www.solarwinds.com/sa-overview/certadvisory</a></p></li><li><p>Cybersecurity and Infrastructure Security Agency. <em>Detecting Post-Compromise Threat Activity in Microsoft Cloud Environments</em>. <a href="https://www.cisa.gov/news-events/cybersecurity-advisories/aa21-008a">https://www.cisa.gov/news-events/cybersecurity-advisories/aa21-008a</a></p></li><li><p>Codecov. <em>Post-Mortem / Root Cause Analysis, April 2021</em>. <a href="https://about.codecov.io/apr-2021-post-mortem/">https://about.codecov.io/apr-2021-post-mortem/</a></p></li><li><p>Codecov. <em>Bash Uploader Security Update</em>. <a href="https://about.codecov.io/security-update/">https://about.codecov.io/security-update/</a></p></li><li><p>CycloneDX. <em>CycloneDX Tool Center</em>. <a href="https://cyclonedx.org/tool-center/">https://cyclonedx.org/tool-center/</a></p></li><li><p>CycloneDX. <em>Authoritative Guide to SBOM</em>. <a href="https://cyclonedx.org/guides/OWASP_CycloneDX-Authoritative-Guide-to-SBOM-en.pdf">https://cyclonedx.org/guides/OWASP_CycloneDX-Authoritative-Guide-to-SBOM-en.pdf</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Stop making developers remember the ritual]]></title><description><![CDATA[A Taskfile turns project knowledge into a small, portable interface your team can actually use]]></description><link>https://emmanuelvalverderamos.substack.com/p/stop-making-developers-remember-the</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/stop-making-developers-remember-the</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 28 Jul 2026 06:00:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!j3MW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every project has a ritual.</p><p>Clone the repository. Install dependencies. Copy the right environment file. Start the database. Run migrations. Start the service. Seed data. Run tests. Build the app. Validate manifests. Format YAML. Parse some JSON. Push a branch. Open a PR. Wait for CI to tell you what you forgot.</p><p>Most teams do not call this a ritual. They call it &#8220;the setup&#8221;. Or &#8220;the usual commands&#8221;. Or &#8220;check the README&#8221;. But the shape is always the same: a set of important steps that live partly in documentation, partly in shell history, partly in CI, partly in the head of the person who has been on the project longest.</p><p>That is bad DevExp.</p><p>Not because developers are lazy. Because every remembered step is cognitive load. Every undocumented command is accidental onboarding cost. Every platform-specific script is a small tax on the team. Every difference between local development and CI is a future debugging session waiting to happen.</p><p>Task helps by giving the project a clear command interface.</p><p>Not a framework. Not a build system that wants to own your architecture. Not a replacement for npm, Docker, Kubernetes, Make, jq, yq or your test runner. Task is a thin, practical layer above the tools you already use. It gives those tools names, structure, dependencies, defaults, checks, prompts, descriptions and cross-platform execution.</p><p>A good Taskfile is not just automation.</p><p>It is executable project knowledge.</p><h2>TL;DR</h2><ul><li><p>Task is a modern, cross-platform task runner configured with <code>Taskfile.yml</code></p></li><li><p>It is especially useful when a project has repeated local, CI, container, Kubernetes, code generation or validation workflows</p></li><li><p>A Taskfile gives developers one stable interface, such as <code>task dev</code>, <code>task test</code>, <code>task verify</code>, <code>task docker:build</code> or <code>task k8s:apply</code></p></li><li><p>It reduces setup friction because commands become discoverable with <code>task --list</code></p></li><li><p>It improves DevExp because project workflows stop depending on shell history, tribal knowledge and platform-specific scripts</p></li><li><p>It supports variables, environment files, dependencies, includes, task aliases, prompts, preconditions, incremental execution, watch mode, loops and templating</p></li><li><p>Dependencies declared with <code>deps</code> run in parallel, so sequential flows should call tasks explicitly from <code>cmds</code></p></li><li><p><code>sources</code>, <code>generates</code> and <code>status</code> are key when you want Task to skip work that is already up to date</p></li><li><p><code>requires</code>, <code>preconditions</code>, <code>prompt</code>, <code>platforms</code> and <code>--dry</code> help make dangerous or unclear workflows safer</p></li><li><p>Task is cross-platform, but the commands you run inside Task may not be</p></li><li><p>User-specific paths should be configurable, not hardcoded into the shared Taskfile</p></li><li><p>Secrets should not live in the Taskfile. Task should orchestrate secret loading from environment files, secret managers, Docker secrets, Kubernetes Secrets or approved platform mechanisms</p></li><li><p>Kubernetes Secrets are not automatically a complete secret-management strategy</p></li><li><p>Large Taskfiles can be split into multiple included Taskfiles, but the project should still feel like one coherent interface</p></li><li><p>A Taskfile should be treated as part of the product&#8217;s engineering interface, not as a dumping ground for random commands</p></li></ul><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Index</h2><ol><li><p>Why DevExp needs a project command interface</p></li><li><p>What Task is</p></li><li><p>What Task is not</p></li><li><p>Where Task fits in the development workflow</p></li><li><p>Installation</p></li><li><p>Your first Taskfile</p></li><li><p>The mental model</p></li><li><p>Anatomy of a Taskfile</p></li><li><p>Task discovery and documentation</p></li><li><p>Variables, environment and dotenv files</p></li><li><p>User-specific paths and home directory configuration</p></li><li><p>Secrets: what Task should and should not do</p></li><li><p>Required inputs and interactive prompts</p></li><li><p>Dependencies and execution order</p></li><li><p>Calling tasks from other tasks</p></li><li><p>Cross-platform workflows</p></li><li><p>Incremental work with sources, generates and status</p></li><li><p>Preconditions, conditional execution and run policies</p></li><li><p>Includes and modular Taskfiles</p></li><li><p>Splitting Taskfiles by responsibility</p></li><li><p>Loops, matrices and repeated work</p></li><li><p>CLI arguments and wildcard tasks</p></li><li><p>Cleanup with defer</p></li><li><p>Output, silence and CI-friendly logs</p></li><li><p>Watch mode</p></li><li><p>Taskfile style guide</p></li><li><p>A complete practical single-file Taskfile</p></li><li><p>A complete modular Taskfile structure</p></li><li><p>Common mistakes</p></li><li><p>How to adopt Task in an existing project</p></li><li><p>Conclusions</p></li></ol><h2>1. Why DevExp needs a project command interface</h2><p>A project without a command interface makes every developer reconstruct the system from pieces.</p><p>One person runs <code>npm test</code>. Another runs <code>npm run test:unit</code>. CI runs something else. Someone validates Kubernetes manifests with <code>kubectl --dry-run=server</code>. Another person uses <code>kubeconform</code>. Someone formats YAML manually. Someone else has a shell alias. The README says one thing, the Makefile says another, the package scripts say another, and the pipeline quietly contains the real truth.</p><p>This is where DevExp gets expensive.</p><p>The problem is not the individual command. The problem is the lack of a shared entry point.</p><p>A good project command interface gives the team a stable vocabulary:</p><pre><code><code>task setup
task dev
task test
task verify
task build
task docker:build
task k8s:validate</code></code></pre><p>The value is not only speed. The value is recoverability.</p><p>A new developer can ask the project what it knows how to do. A senior developer can encode a workflow once instead of explaining it ten times. CI can reuse the same local commands. Documentation can point to task names instead of fragile shell recipes. The project becomes easier to operate because the workflow is explicit.</p><p>That is the core reason Task matters.</p><p>It turns &#8220;ask someone who knows&#8221; into &#8220;run the project interface&#8221;.</p><h2>2. What Task is</h2><p>Task is a task runner configured with a <code>Taskfile.yml</code>.</p><p>It is inspired by Make, but it is designed around modern developer workflows and cross-platform usage. Instead of writing Make syntax, you write YAML. Instead of forcing every project command into a single shell script, you create named tasks with commands, dependencies, descriptions, variables and execution rules.</p><p>A task can be simple:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;d254f677-d50e-4a60-9f77-492a954989d7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  test:
    desc: Run the test suite
    cmds:
      - npm test</code></pre></div><p>Then you run:</p><pre><code><code>task test</code></code></pre><p>That is the smallest useful idea.</p><p>The larger idea is that Task becomes the interface to the project:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;b0431eb2-bb57-4863-9726-3b96d70a1137&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  setup:
    desc: Install project dependencies
    cmds:
      - npm ci

  dev:
    desc: Start the development server
    cmds:
      - npm run dev

  test:
    desc: Run the test suite
    cmds:
      - npm test

  verify:
    desc: Run local checks before pushing
    cmds:
      - task: test
      - task: build

  build:
    desc: Build the application
    cmds:
      - npm run build</code></pre></div><p>A developer does not need to remember whether this project uses npm, pnpm, Docker Compose, kubectl, Helm, jq, yq, a custom script or a mixture of all of them.</p><p>They can start from the task names.</p><h2>3. What Task is not</h2><p>Task is not a replacement for your actual tools.</p><p>It does not replace Docker. It can call Docker.</p><p>It does not replace Kubernetes. It can call kubectl, Helm, kustomize, kubeconform or any validation tool you use.</p><p>It does not replace jq or yq. It can make them part of a repeatable workflow.</p><p>It does not replace npm scripts. It can wrap them behind stable names that survive changes in the underlying implementation.</p><p>It does not replace CI. It can make local and CI commands closer to each other.</p><p>This distinction matters.</p><p>The job of Task is not to become the center of your architecture. The job of Task is to make the useful operations of the project easy to discover, easy to run and hard to misunderstand.</p><p>That is why it works well for DevExp.</p><h2>4. Where Task fits in the development workflow</h2><p>Task sits between the people who need to work with the project and the tools that actually do the work.</p><p>It gives the project a small command surface.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!j3MW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!j3MW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.png 424w, https://substackcdn.com/image/fetch/$s_!j3MW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.png 848w, https://substackcdn.com/image/fetch/$s_!j3MW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.png 1272w, https://substackcdn.com/image/fetch/$s_!j3MW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!j3MW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.png" width="1456" height="1044" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1044,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:116364,&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://emmanuelvalverderamos.substack.com/i/197636466?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.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_!j3MW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.png 424w, https://substackcdn.com/image/fetch/$s_!j3MW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.png 848w, https://substackcdn.com/image/fetch/$s_!j3MW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.png 1272w, https://substackcdn.com/image/fetch/$s_!j3MW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e5186c1-fa0b-42e3-9d88-50d7dfe7f40e_1713x1228.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This diagram matters because it shows the right mental model.</p><p>Task is not the whole delivery system. It is the interface to the delivery system.</p><p>That means a Taskfile should not try to hide everything. It should hide accidental complexity and expose meaningful operations.</p><p><code>task verify</code> is meaningful.</p><p><code>task run-weird-local-thing-that-only-works-on-my-laptop</code> is not.</p><p>The interface should teach the project.</p><h2>5. Installation</h2><p>Task supports several installation methods.</p><p>For macOS or Linux with the official Homebrew tap:</p><pre><code><code>brew install go-task/tap/go-task</code></code></pre><p>With the official Homebrew repository:</p><pre><code><code>brew install go-task</code></code></pre><p>With npm:</p><pre><code><code>npm install -g @go-task/cli</code></code></pre><p>With Winget on Windows:</p><pre><code><code>winget install Task.Task</code></code></pre><p>With Snap:</p><pre><code><code>sudo snap install task --classic</code></code></pre><p>In GitHub Actions:</p><pre><code><code>- name: Install Task
  uses: go-task/setup-task@v1</code></code></pre><p>After installation:</p><pre><code><code>task --version</code></code></pre><p>For teams, the installation choice should be part of the project documentation. If developers use different operating systems, prefer an installation path that works clearly across macOS, Linux and Windows.</p><h2>6. Your first Taskfile</h2><p>Create a <code>Taskfile.yml</code>:</p><pre><code><code>task --init</code></code></pre><p>Or write one manually:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;8e3f0923-e039-4f40-8d3c-81478ee0eb95&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  hello:
    desc: Print a greeting
    cmds:
      - echo "Hello from Task"</code></pre></div><p>Run it:</p><pre><code><code>task hello</code></code></pre><p>A task named <code>default</code> can be executed by running <code>task</code> with no task name:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;199f0e43-0f6e-4193-b826-5a0ecb2582f1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  default:
    desc: Show available tasks
    cmds:
      - task --list
    silent: true

  hello:
    desc: Print a greeting
    cmds:
      - echo "Hello from Task"</code></pre></div><p>Now:</p><pre><code><code>task</code></code></pre><p>prints the available tasks.</p><p>This is a strong default for many projects because it turns the Taskfile into a self-documenting entry point.</p><h2>7. The mental model</h2><p>A Taskfile has four jobs.</p><p>First, it names workflows.</p><p>A name like <code>task verify</code> is easier to remember than a chain of package scripts, shell commands and flags.</p><p>Second, it hides implementation detail.</p><p>A task called <code>test</code> can call <code>npm test</code> today and <code>vitest run</code> tomorrow. The team command stays stable.</p><p>Third, it makes workflows discoverable.</p><p>A developer can run:</p><pre><code><code>task --list</code></code></pre><p>and see the main tasks with descriptions.</p><p>Fourth, it turns repeated project operations into executable documentation.</p><p>This is the real DevExp benefit. The project stops saying &#8220;read this long setup guide and assemble the commands yourself&#8221;. It starts saying &#8220;run these named operations&#8221;.</p><p>That changes the feeling of working with the system.</p><p>A good Taskfile does not only make the happy path shorter. It makes the project more legible.</p><h2>8. Anatomy of a Taskfile</h2><p>A typical Taskfile uses these sections:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;c79a867d-1365-4029-931e-7d48e91b5d76&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

includes:
  docker: ./taskfiles/Docker.yml

vars:
  APP_NAME: checkout-api

env:
  NODE_ENV: development

tasks:
  test:
    desc: Run tests
    cmds:
      - npm test</code></pre></div><p>The usual sections are:</p><ul><li><p><code>version</code> for the Taskfile schema version</p></li><li><p><code>includes</code> for importing other Taskfiles</p></li><li><p><code>vars</code> for Task variables</p></li><li><p><code>env</code> or <code>dotenv</code> for environment variables</p></li><li><p><code>tasks</code> for the actual project commands<br>The official style guide recommends this broad ordering:</p></li></ul><pre><code><code>version:
includes:
# optional configurations
vars:
env:
tasks:</code></code></pre><p>In practice, a clean Taskfile should read like a project interface, not like a random list of shortcuts.</p><p>A useful rule:</p><p>If a developer needs to know it to work on the project, it probably deserves a task</p><h2>9. Task discovery and documentation</h2><p>Task supports descriptions and summaries.</p><p>Descriptions appear in <code>task --list</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;84178e5b-5856-4a61-9e5d-06af62e61034&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  test:
    desc: Run the test suite
    cmds:
      - npm test

  build:
    desc: Build the application
    cmds:
      - npm run build</code></pre></div><p>Run:</p><pre><code><code>task --list</code></code></pre><p>To show all tasks, including tasks without descriptions:</p><pre><code><code>task --list-all</code></code></pre><p>or:</p><pre><code><code>task -a</code></code></pre><p>For longer documentation, use <code>summary</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;dfc4931a-de91-4d5c-99e2-fd6db445fccc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  release:
    desc: Release the service
    summary: |
      Builds, verifies and publishes the service

      Required inputs:
      - VERSION
      - ENV
    cmds:
      - ./scripts/release.sh "{{.VERSION}}" "{{.ENV}}"</code></pre></div><p>Then:</p><pre><code><code>task --summary release</code></code></pre><p>The summary is not executed. It explains.</p><p>This is important. A Taskfile can become part of the teaching material of a project. A new developer should be able to learn the operational shape of the system by listing and summarising tasks.</p><p>The Taskfile is not a substitute for all documentation. It is a bridge between documentation and action.</p><h2>10. Variables, environment and dotenv files</h2><p>Task has variables and environment variables.</p><p>Use <code>vars</code> when you want values for templating:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;0e7032c4-6029-4e74-bc7d-bdb9e140de3e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  APP_NAME: checkout-api
  IMAGE: checkout-api:local

tasks:
  print:
    desc: Print project variables
    cmds:
      - echo "App {{.APP_NAME}}"
      - echo "Image {{.IMAGE}}"</code></pre></div><p>Use <code>env</code> when the executed command needs environment variables:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;fb5b2967-49f7-4f34-80d4-42711b5fec14&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

env:
  NODE_ENV: development

tasks:
  dev:
    desc: Start the app in development mode
    cmds:
      - npm run dev</code></pre></div><p>Use <code>dotenv</code> when the project should load variables from <code>.env</code> style files:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;be223581-c12e-4246-840e-700f0c2e8eb5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

dotenv:
  - .env.local
  - .env

tasks:
  dev:
    desc: Start the app with local environment variables
    cmds:
      - npm run dev</code></pre></div><p>When several dotenv files define the same variable, the first file in the list takes precedence. That makes this pattern useful:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;c92f7d0f-4202-4efb-815d-5f910234d832&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

dotenv:
  - .env.local
  - .env.development
  - .env</code></pre></div><p>The priority is intentional:</p><ul><li><p><code>.env.local</code> for developer-specific overrides</p></li><li><p><code>.env.development</code> for development defaults</p></li><li><p><code>.env</code> for base defaults<br>Be careful with secrets. Task can load <code>.env</code> files, but that does not mean secrets should be committed. Treat the Taskfile as workflow definition, not as a secret store.</p></li></ul><p>There is also a subtle design question here.</p><p>A variable in <code>vars</code> is part of the Taskfile&#8217;s internal templating model. An environment variable in <code>env</code> is part of the environment passed to commands. That distinction keeps Taskfiles easier to understand.</p><p>For example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;2c23dbc6-31e4-4542-8e0a-b0a78c48875c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  APP_NAME: checkout-api

env:
  NODE_ENV: development

tasks:
  dev:
    desc: Start the development server
    cmds:
      - echo "Starting {{.APP_NAME}} with NODE_ENV=$NODE_ENV"
      - npm run dev</code></pre></div><p><code>APP_NAME</code> is a Task variable.</p><p><code>NODE_ENV</code> is an environment variable used by the command.</p><p>Do not mix these concepts accidentally. Use the one that matches the reason the value exists.</p><h2>11. User-specific paths and home directory configuration</h2><p>A Taskfile usually lives in the repository.</p><p>That creates a useful constraint: the Taskfile should describe the shared workflow, but it should not assume that every developer has the same machine.</p><p>This is where many automation files quietly become hostile.</p><p>They contain paths like this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;a335077f-adab-4d5f-a36e-7bacbddfcda6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  open-config:
    cmds:
      - code /home/emmanuel/.config/my-tool/config.yml</code></pre></div><p>That works for one person.</p><p>It fails for everyone else.</p><p>A good Taskfile should separate three things:</p><ul><li><p>Project paths</p></li><li><p>User-specific paths</p></li><li><p>Secrets<br>Project paths belong in the repository.</p></li></ul><p>User-specific paths belong in environment variables, local configuration files or optional local Taskfiles.</p><p>Secrets belong in a secret manager, the local shell environment, Docker secrets, Kubernetes Secrets or another controlled runtime mechanism.</p><p>The Taskfile can orchestrate all of them, but it should not become the place where private machine state or confidential values are stored.</p><h3>Project paths should be relative to the repository</h3><p>Most project paths should be relative.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;f08b3656-c92d-4518-a391-4a2d9ada1e1c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  K8S_DIR: k8s
  DIST_DIR: dist

tasks:
  build:
    desc: Build the application
    cmds:
      - npm run build

  k8s:validate:
    desc: Validate Kubernetes manifests
    cmds:
      - yq eval '.' "{{.K8S_DIR}}"/*.yaml &gt; /dev/null
      - kubectl apply --dry-run=client -f "{{.K8S_DIR}}"</code></pre></div><p>This is portable because the path belongs to the project.</p><p>No developer name. No absolute path. No operating system assumption.</p><p>When you need an absolute project path, Task gives you useful built-in variables:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;31f69be1-6c44-4dbd-b3ed-a70962fcc7f9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  paths:info:
    desc: Print useful Task paths
    cmds:
      - echo "Root directory {{.ROOT_DIR}}"
      - echo "Current Taskfile directory {{.TASKFILE_DIR}}"
      - echo "Task execution directory {{.TASK_DIR}}"
      - echo "User working directory {{.USER_WORKING_DIR}}"</code></pre></div><p>This is especially useful when the Taskfile is included from another directory, or when you run a global Taskfile.</p><h3>Use the home directory carefully</h3><p>Sometimes a task needs something from the user&#8217;s home directory.</p><p>Examples:</p><ul><li><p>A local tool configuration</p></li><li><p>A personal certificate</p></li><li><p>A local kubeconfig</p></li><li><p>A personal cache directory</p></li><li><p>A private <code>.env</code> file<br>The simplest form is this:</p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;0fa88fcd-16c8-49a5-95e3-a309efea732f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

dotenv:
  - .env.local
  - .env
  - '{{.HOME}}/.checkout-api.env'

tasks:
  dev:
    desc: Start the app with project and personal environment files
    cmds:
      - npm run dev</code></pre></div><p>This pattern says:</p><ul><li><p>First read <code>.env.local</code> from the project</p></li><li><p>Then read <code>.env</code> from the project</p></li><li><p>Then read a personal file from the user&#8217;s home directory<br>This can be useful, but be careful. <code>HOME</code> is commonly available in Unix-like environments. On cross-platform teams, it is often cleaner to define explicit local paths through variables.</p></li></ul><p>For example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;52025af7-0ed8-44d3-9570-aa11054b25fe&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  TOOL_CONFIG_FILE: '{{joinPath .HOME ".config" "checkout-api" "config.yml"}}'

tasks:
  config:show:
    desc: Show the resolved local configuration path
    cmds:
      - echo "{{.TOOL_CONFIG_FILE}}"

  config:check:
    desc: Check that the local configuration file exists
    preconditions:
      - sh: test -f "{{.TOOL_CONFIG_FILE}}"
        msg: Create {{.TOOL_CONFIG_FILE}} before running this task
    cmds:
      - echo "Local configuration file exists"</code></pre></div><p>A developer can still override the variable when needed:</p><pre><code><code>task config:show TOOL_CONFIG_FILE="$HOME/dev/configs/checkout-api/config.yml"</code></code></pre><p>The Taskfile no longer needs to know where every user keeps their development files.</p><p>That is the important design move.</p><h3>Use path helpers when paths need to be composed</h3><p>String concatenation works until paths become slightly different across platforms.</p><p>A better approach is to compose paths explicitly:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;c16ea6fa-b0b5-45b7-8559-6aeecac28410&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  LOCAL_CERT_DIR: '{{joinPath .HOME ".certs" "checkout-api"}}'
  LOCAL_CERT_FILE: '{{joinPath .LOCAL_CERT_DIR "dev.crt"}}'

tasks:
  cert:show-path:
    desc: Show the expected local certificate path
    cmds:
      - echo "{{.LOCAL_CERT_FILE}}"

  cert:check:
    desc: Check that the local development certificate exists
    preconditions:
      - sh: test -f "{{.LOCAL_CERT_FILE}}"
        msg: Create {{.LOCAL_CERT_FILE}} before starting HTTPS locally
    cmds:
      - echo "Local certificate is present"</code></pre></div><p>This makes the intention visible.</p><p>The user-specific root is configurable. The rest of the path is constructed by the Taskfile.</p><p>For commands that need forward slashes, Task also provides path conversion helpers:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;5aad34bf-60d0-4692-ba0d-215f322b5837&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  paths:normalize:
    desc: Show path conversions
    vars:
      RAW_PATH: '{{.RAW_PATH | default "C:\\Users\\name\\project"}}'
    cmds:
      - echo "Forward slashes {{.RAW_PATH | toSlash}}"
      - echo "OS-specific slashes {{.RAW_PATH | fromSlash}}"</code></pre></div><p>Use this when a tool expects one path style, but your team uses multiple operating systems.</p><h3>Keep personal overrides outside the shared Taskfile</h3><p>A useful pattern is to support an optional local Taskfile.</p><p>Root <code>Taskfile.yml</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;2b383212-dd31-4486-ba6c-960d37943316&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

includes:
  local:
    taskfile: ./Taskfile.local.yml
    optional: true
    internal: false

tasks:
  default:
    desc: Show available tasks
    cmds:
      - task --list
    silent: true

  dev:
    desc: Start the development server
    cmds:
      - npm run dev</code></pre></div><p>Then add this to <code>.gitignore</code>:</p><pre><code><code>Taskfile.local.yml</code></code></pre><p>A developer can create their own local tasks:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;79455656-8c35-4144-8808-85a1228ed1c9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  tunnel:
    desc: Start my local tunnel
    cmds:
      - cloudflared tunnel run checkout-api-dev

  open-db:
    desc: Open my local database client
    cmds:
      - open -a TablePlus</code></pre></div><p>The shared project interface remains clean.</p><p>The personal workflow is still automated.</p><p>No one else has to inherit one developer&#8217;s machine setup.</p><h3>Use a global Taskfile for personal machine automation</h3><p>Task also supports global Taskfiles.</p><p>When you run:</p><pre><code><code>task -g some-task</code></code></pre><p>Task looks for a Taskfile in your home directory.</p><p>That is useful for personal automation that is not project-specific.</p><p>For example, a developer could keep this at <code>~/Taskfile.yml</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;c712ed86-25fd-4825-8267-cf44c1b1f5fc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  projects:
    desc: Print the projects directory
    cmds:
      - echo "{{.HOME}}/projects"

  clean-docker:
    desc: Clean unused Docker resources
    prompt: Remove unused Docker resources?
    cmds:
      - docker system prune</code></pre></div><p>There is one important detail.</p><p>When running a global Taskfile, tasks run from the home directory by default. If the task should operate on the directory where the command was called, use <code>{{.USER_WORKING_DIR}}</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;e6cf69fa-1baa-429a-8327-bb6df3e2e9eb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  repo:status:
    desc: Show Git status for the directory where task was called
    dir: '{{.USER_WORKING_DIR}}'
    preconditions:
      - sh: test -d .git
        msg: This task must be run inside a Git repository
    cmds:
      - git status --short</code></pre></div><p>Now this works from any repository:</p><pre><code><code>task -g repo:status</code></code></pre><p>This is excellent for personal workflows.</p><p>It should not replace project Taskfiles. The project should still expose its own shared interface.</p><h3>Move Task&#8217;s internal cache when needed</h3><p>Task stores checksum data in a local <code>.task</code> directory by default when using mechanisms such as <code>sources</code> and <code>generates</code>.</p><p>For most repositories, add this to <code>.gitignore</code>:</p><pre><code><code>.task/</code></code></pre><p>Some developers prefer to keep that state outside the project directory.</p><p>They can configure:</p><pre><code><code>export TASK_TEMP_DIR='~/.task'</code></code></pre><p>This is useful when you want repository directories to stay cleaner, or when tooling aggressively watches project files and you do not want <code>.task</code> to appear inside the working tree.</p><p>The Taskfile does not need to change.</p><p>This is a machine-level preference.</p><h2>12. Secrets: what Task should and should not do</h2><p>Task can help with secrets.</p><p>But Task should not become the secret store.</p><p>A Taskfile is normally committed to Git. That means this is wrong:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;6d68cc8b-45ff-4d2e-9cd0-3adeae22ba70&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

env:
  DB_PASSWORD: super-secret-password

tasks:
  dev:
    cmds:
      - npm run dev</code></pre></div><p>It is also risky to commit a real <code>.env</code> file:</p><pre><code><code>DB_USER=checkout
DB_PASSWORD=super-secret-password
STRIPE_SECRET_KEY=sk_live_real_value</code></code></pre><p>The better rule is simple:</p><p>Commit examples, not secrets</p><p>For example, commit <code>.env.example</code>:</p><pre><code><code>DB_USER=checkout
DB_PASSWORD=
STRIPE_SECRET_KEY=</code></code></pre><p>Ignore real local files:</p><pre><code><code>.env
.env.local
.env.*.local</code></code></pre><p>Then let Task load local configuration if it exists:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;d21414d7-a5a7-46ed-a7e5-69e16d893fa8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

dotenv:
  - .env.local
  - .env

tasks:
  dev:
    desc: Start the app with local environment
    preconditions:
      - sh: test -f .env.local
        msg: Create .env.local from .env.example before running the app
    cmds:
      - npm run dev</code></pre></div><p>This is acceptable for low-risk local development secrets, but it is not enough for higher-risk credentials or shared team secrets.</p><p>For those, use a secret manager.</p><p>The boundary should be clear.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yg0y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febbf4f9e-2061-4de0-b812-8ee922600d5e_3026x652.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yg0y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febbf4f9e-2061-4de0-b812-8ee922600d5e_3026x652.png 424w, https://substackcdn.com/image/fetch/$s_!yg0y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febbf4f9e-2061-4de0-b812-8ee922600d5e_3026x652.png 848w, https://substackcdn.com/image/fetch/$s_!yg0y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febbf4f9e-2061-4de0-b812-8ee922600d5e_3026x652.png 1272w, https://substackcdn.com/image/fetch/$s_!yg0y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febbf4f9e-2061-4de0-b812-8ee922600d5e_3026x652.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yg0y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febbf4f9e-2061-4de0-b812-8ee922600d5e_3026x652.png" width="1456" height="314" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ebbf4f9e-2061-4de0-b812-8ee922600d5e_3026x652.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:314,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:119148,&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://emmanuelvalverderamos.substack.com/i/197636466?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febbf4f9e-2061-4de0-b812-8ee922600d5e_3026x652.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_!yg0y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febbf4f9e-2061-4de0-b812-8ee922600d5e_3026x652.png 424w, https://substackcdn.com/image/fetch/$s_!yg0y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febbf4f9e-2061-4de0-b812-8ee922600d5e_3026x652.png 848w, https://substackcdn.com/image/fetch/$s_!yg0y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febbf4f9e-2061-4de0-b812-8ee922600d5e_3026x652.png 1272w, https://substackcdn.com/image/fetch/$s_!yg0y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febbf4f9e-2061-4de0-b812-8ee922600d5e_3026x652.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p><p>Task defines how secrets are loaded.</p><p>Task should not contain the secret values.</p><p>That distinction protects the project from a common failure mode: automation that makes the happy path convenient by making sensitive data too easy to leak.</p><h3>Example 1: Running local development with 1Password CLI</h3><p>A clean pattern is to store secret references in a local file, not raw secret values.</p><p><code>.env.1password</code>:</p><pre><code><code>DB_USER=op://checkout-api-dev/database/username
DB_PASSWORD=op://checkout-api-dev/database/password
STRIPE_SECRET_KEY=op://checkout-api-dev/stripe/secret-key</code></code></pre><p>Then Task can run the app through <code>op run</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;fb280dbc-b15d-4f10-a4a7-2ce377e7bf25&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  dev:secure:
    desc: Start the app with secrets loaded from 1Password
    preconditions:
      - sh: command -v op
        msg: 1Password CLI is required
      - sh: test -f .env.1password
        msg: Create .env.1password with 1Password secret references
    cmds:
      - op run --env-file .env.1password -- npm run dev</code></pre></div><p>This has a better shape than storing the real values in the repository.</p><p>The Taskfile contains the workflow.</p><p>The <code>.env.1password</code> file contains references.</p><p>1Password contains the actual secrets.</p><p>If the team wants to commit the reference file, check that the references do not expose sensitive naming conventions. In many teams, keeping <code>.env.1password.example</code> in Git and <code>.env.1password</code> ignored locally is a safer default.</p><p>Example <code>.env.1password.example</code>:</p><pre><code><code>DB_USER=op://vault/item/username
DB_PASSWORD=op://vault/item/password
STRIPE_SECRET_KEY=op://vault/item/secret-key</code></code></pre><p>Example <code>.gitignore</code>:</p><pre><code><code>.env.1password</code></code></pre><h3>Example 2: Running tests with required secrets</h3><p>Some integration tests require credentials.</p><p>The Taskfile can make that explicit and pass the values to the command environment:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;f2270746-81a3-4688-8fe0-4fddccf6c6b9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  test:integration:
    desc: Run integration tests that require database credentials
    requires:
      vars: [DB_USER, DB_PASSWORD]
    env:
      DB_USER: '{{.DB_USER}}'
      DB_PASSWORD: '{{.DB_PASSWORD}}'
    cmds:
      - npm run test:integration</code></pre></div><p>Run it with variables:</p><pre><code><code>task test:integration DB_USER=checkout DB_PASSWORD=secret</code></code></pre><p>Or with 1Password:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;4ce4bd8e-4c7d-4225-bace-d05440ea4cd5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  test:integration:secure:
    desc: Run integration tests with secrets from 1Password
    preconditions:
      - sh: command -v op
        msg: 1Password CLI is required
      - sh: test -f .env.1password
        msg: Create .env.1password with secret references
    cmds:
      - op run --env-file .env.1password -- task test:integration</code></pre></div><p>This keeps the public task stable.</p><p>The secure variant handles secret injection.</p><p>The important detail is that <code>requires</code> makes the required input explicit, while <code>env</code> ensures the executed command receives the variables it needs.</p><h3>Example 3: Docker Compose secrets for local infrastructure</h3><p>If the project uses Docker Compose, prefer mounting secrets as files when the application supports it.</p><p><code>compose.yml</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;61b75796-aa58-4206-a171-aba57ddedbc1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: checkout
      POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
      POSTGRES_DB: checkout
    secrets:
      - postgres_password
    ports:
      - "5432:5432"

secrets:
  postgres_password:
    file: ./secrets/postgres_password.txt</code></pre></div><p><code>.gitignore</code>:</p><pre><code><code>secrets/</code></code></pre><p>Taskfile:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;86c342d4-a290-4e0e-a8f1-fb71315ee3c0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  compose:up:
    desc: Start local infrastructure with Docker Compose secrets
    preconditions:
      - sh: command -v docker
        msg: Docker is required
      - sh: test -f secrets/postgres_password.txt
        msg: Create secrets/postgres_password.txt before starting Compose
    cmds:
      - docker compose up -d</code></pre></div><p>This pattern avoids putting the secret value in the Compose file.</p><p>The secret is still local, so protect the file properly. But the repository no longer contains the secret.</p><p>For onboarding, provide a safe setup task:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;92a93f2a-61de-45a6-8680-a360d91de20a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  secrets:init:
    desc: Create local secret files for development
    cmds:
      - mkdir -p secrets
      - test -f secrets/postgres_password.txt || printf "checkout-dev-password\n" &gt; secrets/postgres_password.txt
      - cmd: chmod 600 secrets/postgres_password.txt
        platforms: [linux, darwin]</code></pre></div><p>This is suitable only for local development credentials.</p><p>Do not use a generated shared password like this for production.</p><p>The <code>chmod</code> command is limited to Linux and macOS because it is not a native PowerShell command. This is an example of an important principle: Task is cross-platform, but every command inside a task still has its own platform behaviour.</p><h3>Example 4: Docker build secrets</h3><p>Sometimes the build needs access to a private package registry.</p><p>Do not bake the token into the image.</p><p>Bad:</p><pre><code><code>ARG NPM_TOKEN
RUN npm config set //registry.npmjs.org/:_authToken=$NPM_TOKEN</code></code></pre><p>Better pattern with Docker BuildKit secrets:</p><pre><code><code>RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci</code></code></pre><p>Taskfile:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;ab6fdfaa-94b2-4f44-956f-7a5f919c91ee&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  docker:build:
    desc: Build the image using a local npm secret
    preconditions:
      - sh: test -f "{{.HOME}}/.npmrc"
        msg: ~/.npmrc is required for private package installation
    cmds:
      - docker build --secret id=npmrc,src="{{.HOME}}/.npmrc" -t checkout-api:local .</code></pre></div><p>The secret is available to the build step as a mounted secret.</p><p>It is not written into the Taskfile.</p><p>It is not passed as a normal build argument.</p><p>It is not intended to remain in the final image.</p><h3>Example 5: Creating a Kubernetes Secret from local files</h3><p>For Kubernetes, the Taskfile can create a Secret from local files.</p><p>First, keep the local secret file out of Git:</p><pre><code><code>.local-secrets/</code></code></pre><p>Local file:</p><pre><code><code>.local-secrets/db-password</code></code></pre><p>Taskfile:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;0e81c015-f031-4aac-9019-b577500d5c19&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  NAMESPACE: dev
  SECRET_NAME: checkout-api-secrets

tasks:
  k8s:secret:create:
    desc: Create or update the app Kubernetes Secret from local files
    preconditions:
      - sh: command -v kubectl
        msg: kubectl is required
      - sh: test -f .local-secrets/db-password
        msg: Create .local-secrets/db-password first
    cmds:
      - |
        kubectl create secret generic "{{.SECRET_NAME}}" \
          --namespace "{{.NAMESPACE}}" \
          --from-file=db-password=.local-secrets/db-password \
          --dry-run=client \
          -o yaml | kubectl apply -f -</code></pre></div><p>Then the deployment can consume the secret.</p><p>Example Kubernetes fragment:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;1d5818e8-b3da-45fb-b428-253337f969e4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">apiVersion: apps/v1
kind: Deployment
metadata:
  name: checkout-api
spec:
  template:
    spec:
      containers:
        - name: checkout-api
          image: checkout-api:local
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: checkout-api-secrets
                  key: db-password</code></pre></div><p>This is a practical local development pattern.</p><p>For production, the better pattern usually involves a controlled secret delivery mechanism, such as a cloud secret manager, External Secrets Operator, Sealed Secrets, SOPS, Vault or your platform&#8217;s approved secret workflow.</p><p>A Kubernetes Secret is not automatically a complete secret-management strategy. By default, Kubernetes stores Secrets in the API server data store without encryption at rest unless encryption at rest and access control are configured properly.</p><p>Task can orchestrate the workflow, but it should not invent the security model.</p><h3>Example 6: Creating a Kubernetes Secret from 1Password at runtime</h3><p>You can combine Task, 1Password and kubectl.</p><p><code>.env.1password</code>:</p><pre><code><code>DB_PASSWORD=op://checkout-api-dev/database/password</code></code></pre><p>Taskfile:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;c8f25379-0290-44cf-8fcd-d3813c8784f9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  NAMESPACE: dev
  SECRET_NAME: checkout-api-secrets

tasks:
  k8s:secret:create-from-1password:
    desc: Create or update the app Kubernetes Secret from 1Password
    preconditions:
      - sh: command -v op
        msg: 1Password CLI is required
      - sh: command -v kubectl
        msg: kubectl is required
      - sh: test -f .env.1password
        msg: Create .env.1password with secret references
    cmds:
      - |
        op run --env-file .env.1password -- sh -c '
          kubectl create secret generic "{{.SECRET_NAME}}" \
            --namespace "{{.NAMESPACE}}" \
            --from-literal=db-password="$DB_PASSWORD" \
            --dry-run=client \
            -o yaml | kubectl apply -f -
        '</code></pre></div><p>This keeps the secret out of the repository and out of the Taskfile.</p><p>The secret exists in the subprocess environment while the command runs.</p><p>The resulting Kubernetes Secret exists in the cluster.</p><p>This is still sensitive. Access to the cluster and namespace now matters. Anyone with enough permission to read that Secret can access the value.</p><p>Task makes the workflow repeatable. It does not remove the need for proper access control.</p><h3>Example 7: Validating that secrets exist without printing them</h3><p>A common mistake is to debug by printing secrets.</p><p>Do not do this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;4ed63739-4bdb-4131-9ec4-551377d1bbc0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  debug-secret:
    cmds:
      - echo "$DB_PASSWORD"</code></pre></div><p>Instead, validate presence without revealing the value:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;f19fbb67-ca4a-48dc-a839-d55d34c50d12&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  secrets:check:
    desc: Check required secret variables without printing values
    requires:
      vars: [DB_PASSWORD, STRIPE_SECRET_KEY]
    cmds:
      - echo "Required secret variables are present"</code></pre></div><p>Or with shell checks:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;822aec58-c31f-4e3c-adde-d97faa6b2413&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  secrets:check:
    desc: Check required secret variables without printing values
    cmds:
      - test -n "$DB_PASSWORD"
      - test -n "$STRIPE_SECRET_KEY"
      - echo "Required secret variables are present"</code></pre></div><p>The task should prove readiness.</p><p>It should not leak values into the terminal, CI logs or shared screenshots.</p><h2>13. Required inputs and interactive prompts</h2><p>Some tasks should not run without explicit input.</p><p>A deployment task is a good example. Running it without <code>ENV</code> or <code>VERSION</code> should fail early with a clear message, not halfway through a script.</p><p>Task supports <code>requires</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;67a46109-13f8-48a3-9db8-0117f667c4e7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  deploy:
    desc: Deploy a specific version
    requires:
      vars: [ENV, VERSION]
    cmds:
      - ./scripts/deploy.sh "{{.ENV}}" "{{.VERSION}}"</code></pre></div><p>Run:</p><pre><code><code>task deploy ENV=staging VERSION=1.4.2</code></code></pre><p>You can restrict a variable to allowed values:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;e7d0c370-46e8-468d-9449-2f4e571cd60b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  deploy:
    desc: Deploy a specific version to a known environment
    requires:
      vars:
        - name: ENV
          enum: [dev, staging, prod]
        - VERSION
    cmds:
      - ./scripts/deploy.sh "{{.ENV}}" "{{.VERSION}}"</code></pre></div><p>You can also define allowed values once and reuse them:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;a9cdb0aa-3ba3-4718-a0de-08061ced4966&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  ALLOWED_ENVS: [dev, staging, prod]

tasks:
  deploy:
    desc: Deploy a specific version
    requires:
      vars:
        - name: ENV
          enum:
            ref: .ALLOWED_ENVS
        - VERSION
    cmds:
      - ./scripts/deploy.sh "{{.ENV}}" "{{.VERSION}}"</code></pre></div><p>Interactive mode can prompt users for missing required variables when a terminal is available:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;ee10bfb6-a23e-4a3c-959d-525df75843ba&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  deploy:
    desc: Deploy a version interactively when inputs are missing
    requires:
      vars:
        - name: ENV
          enum: [dev, staging, prod]
        - VERSION
    cmds:
      - echo "Deploying {{.VERSION}} to {{.ENV}}"</code></pre></div><p>Run:</p><pre><code><code>task deploy --interactive</code></code></pre><p>This is useful for local workflows, but CI should pass variables explicitly.</p><p>For dangerous tasks, use warning prompts:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;a00937c2-d669-49ab-b11e-50b518e760f4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  prod:delete:
    desc: Delete production resources
    prompt:
      - This will delete production resources. Continue?
      - Are you completely sure?
    cmds:
      - ./scripts/delete-production-resources.sh</code></pre></div><p>Prompts are not a security boundary. They are a friction point that helps avoid accidental execution.</p><p>For CI, avoid interactive assumptions. Pass values explicitly, use non-interactive configuration and be careful with prompt-skipping flags.</p><h2>14. Dependencies and execution order</h2><p>Task supports dependencies with <code>deps</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;383ba794-4105-429f-a7cf-d5cd8d5504c6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  build:
    desc: Build the application
    deps: [assets]
    cmds:
      - npm run build

  assets:
    desc: Build frontend assets
    cmds:
      - npm run build:assets</code></pre></div><p>When you run:</p><pre><code><code>task build</code></code></pre><p>Task runs <code>assets</code> first.</p><p>The most important rule is this:</p><p>Dependencies run in parallel when there is more than one dependency</p><p>That is good for independent work:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;3529d171-db67-4170-8c85-9544d3a741f6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  assets:
    desc: Build all assets
    deps: [js, css]

  js:
    cmds:
      - npm run build:js

  css:
    cmds:
      - npm run build:css</code></pre></div><p>But it is wrong for workflows that need strict order.</p><p>This can be wrong:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;da484714-c261-4058-b3fe-1165f7849c09&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown">version: '3'

tasks:
  verify:
    deps: [lint, test, build]</code></pre></div><p>If <code>build</code> depends on generated files from <code>test</code> or <code>lint</code>, this is not the right structure.</p><p>Use explicit task calls inside <code>cmds</code> when order matters:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;11a43799-dbb1-4f1b-82e3-8ca0b9bc4898&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  verify:
    desc: Run checks in a deliberate order
    cmds:
      - task: lint
      - task: test
      - task: build

  lint:
    cmds:
      - npm run lint

  test:
    cmds:
      - npm test

  build:
    cmds:
      - npm run build</code></pre></div><p>This is one of the most important design decisions in Taskfiles:</p><p>Use <code>deps</code> for independent prerequisites</p><p>Use <code>cmds</code> with <code>task:</code> for sequential workflows</p><h2>15. Calling tasks from other tasks</h2><p>A task can call another task:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;b811075b-a75b-4238-b30b-aeb9e31ec71f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  verify:
    desc: Run all verification steps
    cmds:
      - task: lint
      - task: test
      - task: build

  lint:
    cmds:
      - npm run lint

  test:
    cmds:
      - npm test

  build:
    cmds:
      - npm run build</code></pre></div><p>You can pass variables:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;385633f8-ab65-40ba-8400-de0e09714c35&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  docker:build:
    desc: Build a Docker image
    vars:
      IMAGE: checkout-api:local
    cmds:
      - docker build -t "{{.IMAGE}}" .

  docker:build:prod:
    desc: Build the production Docker image
    cmds:
      - task: docker:build
        vars:
          IMAGE: checkout-api:prod</code></pre></div><p>This keeps the public interface small while allowing reuse.</p><p>A useful pattern is to create internal tasks for shared implementation details.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;3ceafe16-d96d-4141-8b43-d55a1bd8918f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  docker:build:
    desc: Build the local Docker image
    cmds:
      - task: _docker-build
        vars:
          IMAGE: checkout-api:local

  docker:build:prod:
    desc: Build the production Docker image
    cmds:
      - task: _docker-build
        vars:
          IMAGE: checkout-api:prod

  _docker-build:
    internal: true
    requires:
      vars: [IMAGE]
    cmds:
      - docker build -t "{{.IMAGE}}" .</code></pre></div><p>The public tasks express intent. The internal task holds the implementation.</p><p>That is good interface design.</p><h2>16. Cross-platform workflows</h2><p>Task is valuable because it helps a team avoid maintaining separate local workflows for macOS, Linux and Windows.</p><p>There are several practical rules.</p><p>First, prefer task names over shell aliases.</p><p>A shell alias is personal. A task is part of the repository.</p><p>Second, avoid putting too much shell logic directly in the Taskfile.</p><p>If logic becomes complex, move it to a script:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;80ce42bd-06af-46fe-bd95-24effd18c551&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  migrate:
    desc: Run database migrations
    cmds:
      - ./scripts/migrate.sh</code></pre></div><p>Third, use platform filters when a command is genuinely platform-specific:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;447b42c8-839f-43e4-b54c-21b60f7e0c16&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  open:
    desc: Open the local app in a browser
    cmds:
      - cmd: open http://localhost:3000
        platforms: [darwin]
      - cmd: xdg-open http://localhost:3000
        platforms: [linux]
      - cmd: start http://localhost:3000
        platforms: [windows]</code></pre></div><p>Fourth, use Task template functions for paths and executable extensions when needed:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;80c171a7-bd1c-4806-9e06-f6cc22ea64f6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  build:
    desc: Build a binary with the right extension for the platform
    cmds:
      - go build -o "bin/app{{exeExt}}" ./cmd/app</code></pre></div><p>Fifth, treat platform support as a design decision.</p><p>Task is cross-platform, but the commands you orchestrate may not be. If a workflow must work on native Windows, macOS and Linux, either use platform-specific commands with <code>platforms</code>, or move the logic to a script written in a cross-platform runtime already used by the project.</p><p>For example, if your project already uses Node.js, a script like this can be more portable than shell-specific file logic:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;8edb983c-bfe8-4f7f-b638-93a84ac503c1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import { mkdirSync, existsSync, writeFileSync, chmodSync } from "node:fs";
import { join } from "node:path";

const secretsDir = ".local-secrets";
const passwordFile = join(secretsDir, "db-password");

mkdirSync(secretsDir, { recursive: true });

if (!existsSync(passwordFile)) {
  writeFileSync(passwordFile, "checkout-dev-password\n", { encoding: "utf8" });
}

try {
  chmodSync(passwordFile, 0o600);
} catch {
  // Some platforms or filesystems may not support POSIX permissions
}</code></pre></div><p>Then Task can call the script:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;12ad409b-155e-4570-9888-16f55ebc95fb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  secrets:init:
    desc: Create local development secret files
    cmds:
      - node scripts/init-local-secrets.js</code></pre></div><p>That is often a better DevExp than filling the Taskfile with platform-specific shell branches.</p><h2>17. Incremental work with sources, generates and status</h2><p>Some tasks do expensive work.</p><p>Build assets. Generate code. Build an image. Compile binaries. Produce documentation. Validate many files.</p><p>Task can skip work when inputs have not changed.</p><p>Use <code>sources</code> to describe inputs and <code>generates</code> to describe outputs:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;6a460a55-f77b-4589-8247-96a40c3ca94c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  build:
    desc: Build only when source files changed
    sources:
      - package.json
      - package-lock.json
      - 'src/**/*.js'
    generates:
      - dist/server.js
    cmds:
      - npm run build</code></pre></div><p>By default, Task uses checksums. You can use timestamps instead:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;2965f217-e813-41eb-a9b1-6ff641b03397&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  build:
    desc: Build using timestamp checks
    method: timestamp
    sources:
      - package.json
      - package-lock.json
      - 'src/**/*.js'
    generates:
      - dist/server.js
    cmds:
      - npm run build</code></pre></div><p>You can configure the method globally:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;0bf2f213-2ce0-4707-a238-f3911e456829&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

method: timestamp

tasks:
  build:
    sources:
      - 'src/**/*.js'
    generates:
      - dist/server.js
    cmds:
      - npm run build</code></pre></div><p>Use <code>status</code> when the task&#8217;s output is not a simple local file.</p><p>For example, a Docker image:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;178452cb-ce92-4747-a3fd-7160d715a07e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  docker:build:
    desc: Build the Docker image if it is missing
    vars:
      IMAGE: checkout-api:local
    status:
      - docker image inspect "{{.IMAGE}}" &gt; /dev/null 2&gt;&amp;1
    cmds:
      - docker build -t "{{.IMAGE}}" .</code></pre></div><p>If the status command returns success, Task treats the task as up to date.</p><p>You can force execution:</p><pre><code><code>task docker:build --force</code></code></pre><p>You can check status without running the task:</p><pre><code><code>task docker:build --status</code></code></pre><p>This is useful in CI and local workflows because it makes expensive work conditional instead of habitual.</p><h2>18. Preconditions, conditional execution and run policies</h2><p><code>status</code> answers this question:</p><p>Is this task already done?</p><p><code>preconditions</code> answer a different question:</p><p>Is it valid to run this task now?</p><p>Example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;4d49b85d-b2ee-4726-b73a-19e739df0ecf&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  dev:
    desc: Start the development server
    preconditions:
      - sh: test -f package.json
        msg: Run this task from a project with package.json
      - sh: test -f .env.local
        msg: Create .env.local before starting the service
    cmds:
      - npm run dev</code></pre></div><p>If a precondition fails, the task fails before running its commands.</p><p>Use this for required files, required tools, expected directories, local configuration and dangerous assumptions.</p><p>Task also supports <code>if</code>, which skips instead of failing:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;9c459201-c9d4-43e8-b9c2-5eba86844ba4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  verify:
    desc: Run optional checks when their configuration exists
    cmds:
      - cmd: npm run lint
        if: test -f package.json
      - cmd: yq --version
        if: command -v yq</code></pre></div><p>Use <code>if</code> when skipping is acceptable.</p><p>Use <code>preconditions</code> when skipping would hide a real problem.</p><p>Task also supports <code>run</code> policies:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;979af170-59de-442a-a2f3-8e71e099e56c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  setup:
    run: once
    cmds:
      - npm ci

  generate:
    run: when_changed
    cmds:
      - echo "{{.TARGET}}"</code></pre></div><p>The main options are:</p><ul><li><p><code>always</code> to attempt the task every time</p></li><li><p><code>once</code> to run only once within a Task invocation</p></li><li><p><code>when_changed</code> to run once for each unique set of variables<br>This becomes useful when one task is called multiple times by other tasks.</p></li></ul><h2>19. Includes and modular Taskfiles</h2><p>A single Taskfile can become too large.</p><p>Task supports includes:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;18eac871-428a-4405-bb6e-e4f1b3d0442a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

includes:
  docker: ./taskfiles/Docker.yml
  k8s: ./taskfiles/Kubernetes.yml
  docs: ./taskfiles/Docs.yml

tasks:
  verify:
    desc: Run project verification
    cmds:
      - task: docker:build
      - task: k8s:validate
      - task: docs:check</code></pre></div><p>If <code>taskfiles/Docker.yml</code> contains:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;91bdc3cc-bf1b-4dcf-abd9-62fc425984e2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  build:
    desc: Build the Docker image
    cmds:
      - docker build -t checkout-api:local .</code></pre></div><p>Then you run:</p><pre><code><code>task docker:build</code></code></pre><p>Includes are especially useful in monorepos:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;e666db28-67e5-4f28-94f9-3fa9dd0224f1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

includes:
  api:
    taskfile: ./services/api/Taskfile.yml
    dir: ./services/api

  web:
    taskfile: ./services/web/Taskfile.yml
    dir: ./services/web</code></pre></div><p>Then:</p><pre><code><code>task api:test
task web:test</code></code></pre><p>You can pass variables to included Taskfiles:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;f457edfe-04de-4f55-aa16-7c88dc37edc3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

includes:
  backend:
    taskfile: ./taskfiles/Docker.yml
    vars:
      DOCKER_IMAGE: backend_image

  frontend:
    taskfile: ./taskfiles/Docker.yml
    vars:
      DOCKER_IMAGE: frontend_image</code></pre></div><p>You can also make includes optional:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;1b70b7a3-f83d-45ba-bc3d-efdd1dabd02a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

includes:
  local:
    taskfile: ./Taskfile.local.yml
    optional: true</code></pre></div><p>This is useful when each developer may have private local tasks that should not be required by the project.</p><p>Use includes when they improve navigation. Do not split a Taskfile just because it is possible. Split by responsibility:</p><ul><li><p>Docker tasks</p></li><li><p>Kubernetes tasks</p></li><li><p>Documentation tasks</p></li><li><p>Code generation tasks</p></li><li><p>Release tasks</p></li><li><p>Service-specific tasks in a monorepo<br>One important detail: root-level <code>dotenv</code> declarations cannot currently live inside included Taskfiles. Put shared dotenv loading in the main Taskfile, or use task-level dotenv where that is the right local choice.</p></li></ul><h2>20. Splitting Taskfiles by responsibility</h2><p>At some point, a Taskfile stops feeling like a project interface and starts feeling like a long utility drawer.</p><p>That is the moment to split it.</p><p>Not before.</p><p>Splitting Taskfiles is useful when it makes the workflow easier to understand. It is harmful when it forces people to jump across files just to understand a simple project.</p><p>A good split keeps the main <code>Taskfile.yml</code> as the entrance to the project.</p><pre><code><code>.
&#9500;&#9472;&#9472; Taskfile.yml
&#9500;&#9472;&#9472; taskfiles
&#9474;   &#9500;&#9472;&#9472; Dev.yml
&#9474;   &#9500;&#9472;&#9472; Quality.yml
&#9474;   &#9500;&#9472;&#9472; Docker.yml
&#9474;   &#9500;&#9472;&#9472; Kubernetes.yml
&#9474;   &#9500;&#9472;&#9472; Secrets.yml
&#9474;   &#9492;&#9472;&#9472; Docs.yml
&#9500;&#9472;&#9472; package.json
&#9500;&#9472;&#9472; Dockerfile
&#9500;&#9472;&#9472; compose.yml
&#9492;&#9472;&#9472; k8s
    &#9500;&#9472;&#9472; deployment.yaml
    &#9492;&#9472;&#9472; service.yaml</code></code></pre><p>The root Taskfile should explain the project&#8217;s main operations:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;96a69abd-ab67-4456-a525-6e1b240b9820&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  APP_NAME: checkout-api
  NAMESPACE: dev
  IMAGE: checkout-api:local

dotenv:
  - .env.local
  - .env

includes:
  dev:
    taskfile: ./taskfiles/Dev.yml
  quality:
    taskfile: ./taskfiles/Quality.yml
  docker:
    taskfile: ./taskfiles/Docker.yml
    vars:
      IMAGE: '{{.IMAGE}}'
  k8s:
    taskfile: ./taskfiles/Kubernetes.yml
    vars:
      APP_NAME: '{{.APP_NAME}}'
      NAMESPACE: '{{.NAMESPACE}}'
  secrets:
    taskfile: ./taskfiles/Secrets.yml
  docs:
    taskfile: ./taskfiles/Docs.yml

tasks:
  default:
    desc: Show available tasks
    cmds:
      - task --list
    silent: true

  setup:
    desc: Prepare the project for local development
    cmds:
      - task: dev:setup
      - task: secrets:init

  dev:
    desc: Start the local development environment
    cmds:
      - task: dev:start

  verify:
    desc: Run all checks before pushing
    cmds:
      - task: quality:lint
      - task: quality:test
      - task: quality:build
      - task: docker:build
      - task: k8s:validate</code></pre></div><p>This root file gives the reader the story.</p><p>Setup.</p><p>Development.</p><p>Verification.</p><p>Everything else is delegated.</p><h3>Dev.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;1888c5d4-c8ec-4e94-bbf3-d19be262356c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  setup:
    desc: Install project dependencies
    sources:
      - package.json
      - package-lock.json
    generates:
      - node_modules/.package-lock.json
    cmds:
      - npm ci

  start:
    desc: Start the development server
    deps: [setup]
    preconditions:
      - sh: test -f package.json
        msg: package.json is required
      - sh: test -f .env.local
        msg: Create .env.local from .env.example before starting the app
    cmds:
      - npm run dev</code></pre></div><h3>Quality.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;00008690-145a-4b9e-8e8c-bc963fe52682&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  lint:
    desc: Run linting
    cmds:
      - npm run lint

  test:
    desc: Run the test suite
    cmds:
      - npm test

  build:
    desc: Build the application
    sources:
      - package.json
      - package-lock.json
      - 'src/**/*.js'
    generates:
      - dist/server.js
    cmds:
      - npm run build</code></pre></div><h3>Docker.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;8446dd02-ec9b-4683-971a-36bc3f710013&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  IMAGE: checkout-api:local

tasks:
  build:
    desc: Build the local Docker image
    sources:
      - Dockerfile
      - package.json
      - package-lock.json
      - 'src/**/*.js'
    cmds:
      - docker build -t "{{.IMAGE}}" .

  build-private:
    desc: Build the Docker image using a private npm configuration as a BuildKit secret
    preconditions:
      - sh: test -f "{{.HOME}}/.npmrc"
        msg: ~/.npmrc is required for private package installation
    cmds:
      - docker build --secret id=npmrc,src="{{.HOME}}/.npmrc" -t "{{.IMAGE}}" .

  run:
    desc: Run the Docker image locally
    deps: [build]
    cmds:
      - docker run --rm -p 3000:3000 "{{.IMAGE}}"

  compose-up:
    desc: Start local infrastructure
    cmds:
      - docker compose up -d

  compose-down:
    desc: Stop local infrastructure
    cmds:
      - docker compose down</code></pre></div><h3>Kubernetes.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;868f387a-ce53-4521-a37e-dc4a46ab3682&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  K8S_DIR: k8s
  NAMESPACE: dev
  APP_NAME: checkout-api

tasks:
  validate:
    desc: Validate Kubernetes manifests locally
    preconditions:
      - sh: command -v yq
        msg: yq is required
      - sh: command -v kubectl
        msg: kubectl is required
      - sh: test -d "{{.K8S_DIR}}"
        msg: Kubernetes directory not found
    cmds:
      - yq eval '.' "{{.K8S_DIR}}"/*.yaml &gt; /dev/null
      - kubectl apply --dry-run=client -f "{{.K8S_DIR}}"

  apply:
    desc: Apply Kubernetes manifests to a namespace
    prompt:
      - Apply manifests to namespace {{.NAMESPACE}}?
    cmds:
      - kubectl create namespace "{{.NAMESPACE}}" --dry-run=client -o yaml | kubectl apply -f -
      - kubectl apply -n "{{.NAMESPACE}}" -f "{{.K8S_DIR}}"

  pods:
    desc: List pods using jq
    preconditions:
      - sh: command -v jq
        msg: jq is required
      - sh: command -v kubectl
        msg: kubectl is required
    cmds:
      - kubectl get pods -n "{{.NAMESPACE}}" -o json | jq '.items[] | {name: .metadata.name, phase: .status.phase}'

  logs:
    desc: Show logs for the app deployment
    cmds:
      - kubectl logs -n "{{.NAMESPACE}}" "deploy/{{.APP_NAME}}"

  delete:
    desc: Delete Kubernetes manifests from a namespace
    prompt:
      - Delete manifests from namespace {{.NAMESPACE}}?
    cmds:
      - kubectl delete -n "{{.NAMESPACE}}" -f "{{.K8S_DIR}}"</code></pre></div><p><code>kubectl apply --dry-run=client</code> is useful as a fast local check, but it does not replace server-side validation against the real cluster API.</p><h3>Secrets.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;dac18d50-7360-4f8a-806a-7bd922a19b36&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  LOCAL_SECRETS_DIR: .local-secrets
  DB_PASSWORD_FILE: '{{joinPath .LOCAL_SECRETS_DIR "db-password"}}'

tasks:
  init:
    desc: Create local development secret files
    cmds:
      - mkdir -p "{{.LOCAL_SECRETS_DIR}}"
      - test -f "{{.DB_PASSWORD_FILE}}" || printf "checkout-dev-password\n" &gt; "{{.DB_PASSWORD_FILE}}"
      - cmd: chmod 600 "{{.DB_PASSWORD_FILE}}"
        platforms: [linux, darwin]

  check:
    desc: Check required local secret files without printing values
    preconditions:
      - sh: test -f "{{.DB_PASSWORD_FILE}}"
        msg: Create {{.DB_PASSWORD_FILE}} first
    cmds:
      - echo "Required local secret files are present"

  dev-secure:
    desc: Run the app with secrets loaded from 1Password
    preconditions:
      - sh: command -v op
        msg: 1Password CLI is required
      - sh: test -f .env.1password
        msg: Create .env.1password with 1Password secret references
    cmds:
      - op run --env-file .env.1password -- npm run dev</code></pre></div><h3>Docs.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;a7328a0d-7194-484f-b92f-a5be8d4761d4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  serve:
    desc: Serve documentation locally
    cmds:
      - npm run docs:serve

  check:
    desc: Check documentation quality
    cmds:
      - npm run docs:check</code></pre></div><p>This structure creates a clean interface:</p><pre><code><code>task setup
task dev
task verify
task docker:build
task docker:compose-up
task k8s:validate
task k8s:pods
task secrets:dev-secure
task docs:serve</code></code></pre><p>The project feels like one interface, even though the implementation is split across several files.</p><p>That is the point.</p><p>A split Taskfile should still feel like one project interface. The goal is not to create many files. The goal is to keep each responsibility easy to find, easy to reason about and easy to change.</p><h3>When not to split</h3><p>Do not split a Taskfile just because you can.</p><p>A tiny project does not need this:</p><pre><code><code>taskfiles/Build.yml
taskfiles/Test.yml
taskfiles/Dev.yml
taskfiles/Clean.yml</code></code></pre><p>when the full Taskfile is only this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;2a43ba38-fa3d-4755-8f00-9d63c0b47b02&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  dev:
    desc: Start development
    cmds:
      - npm run dev

  test:
    desc: Run tests
    cmds:
      - npm test

  build:
    desc: Build the app
    cmds:
      - npm run build</code></pre></div><p>Splitting has a carrying cost.</p><p>It adds navigation. It adds indirection. It adds more places to look.</p><p>Split when the structure reduces cognitive load, not when it merely looks more architectural.</p><h2>21. Loops, matrices and repeated work</h2><p>Task supports loops.</p><p>A simple list:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;a517924c-2995-43eb-bf4e-054cefab852a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  print-services:
    desc: Print service names
    cmds:
      - for: [api, web, worker]
        cmd: echo "{{.ITEM}}"</code></pre></div><p>A variable:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;ccf65996-1ce6-437f-bca7-095ce99f723a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  SERVICES: [api, web, worker]

tasks:
  print-services:
    desc: Print service names from a variable
    cmds:
      - for:
          var: SERVICES
        cmd: echo "{{.ITEM}}"</code></pre></div><p>A renamed iterator:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;9c3a0d9f-4198-4daf-82e0-3342231e7645&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  SERVICES: [api, web, worker]

tasks:
  print-services:
    desc: Print service names with a clear variable name
    cmds:
      - for:
          var: SERVICES
          as: SERVICE
        cmd: echo "{{.SERVICE}}"</code></pre></div><p>A matrix:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;cf032585-6cd4-4ffa-93c2-9c79140c35bb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  test:matrix:
    desc: Print a test matrix
    cmds:
      - for:
          matrix:
            NODE_VERSION: ['20', '22']
            TEST_TYPE: [unit, integration]
        cmd: echo "Run {{.ITEM.TEST_TYPE}} tests on Node {{.ITEM.NODE_VERSION}}"</code></pre></div><p>Loops also work with dependencies:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;a8b028ad-2435-42f2-ac97-60b34c9f7e48&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  build:all:
    desc: Build all services in parallel
    deps:
      - for: [api, web, worker]
        task: build
        vars:
          SERVICE: '{{.ITEM}}'

  build:
    internal: true
    requires:
      vars: [SERVICE]
    cmds:
      - echo "Building {{.SERVICE}}"</code></pre></div><p>Remember the same rule as before:</p><p>Looping inside <code>deps</code> means parallel execution</p><p>That is great for independent service builds. It is not right for ordered deployment steps.</p><h2>22. CLI arguments and wildcard tasks</h2><p>Sometimes a task should forward arguments to another command.</p><p>Task supports <code>--</code> and exposes the rest as <code>.CLI_ARGS</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;4a29aa3e-863b-49aa-a38e-3290e3ee87a1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  test:
    desc: Run tests and forward extra arguments
    cmds:
      - npm test -- {{.CLI_ARGS}}</code></pre></div><p>Run:</p><pre><code><code>task test -- --watch</code></code></pre><p>For Kubernetes logs:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;ba313af4-7673-4057-998f-7be970fad822&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  k8s:logs:
    desc: Forward arguments to kubectl logs
    cmds:
      - kubectl logs {{.CLI_ARGS}}</code></pre></div><p>Run:</p><pre><code><code>task k8s:logs -- deploy/checkout-api -n dev</code></code></pre><p>Task also supports wildcard task names.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;406318d3-8b20-476d-8e65-bd45e6d761ec&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  service:*:logs:
    desc: Show logs for a service
    vars:
      SERVICE: '{{index .MATCH 0}}'
    cmds:
      - kubectl logs "deploy/{{.SERVICE}}" -n dev</code></pre></div><p>Run:</p><pre><code><code>task service:checkout-api:logs</code></code></pre><p>This can be elegant, but use it carefully. Wildcards are less discoverable than explicit task names. They are useful when the pattern is obvious and stable.</p><h2>23. Cleanup with defer</h2><p>Some tasks create temporary files, start services or create local state that should be cleaned up.</p><p>Task supports <code>defer</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;8e5ec954-efbe-44ae-a70f-1c75bbe6885f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  demo:
    desc: Create a temporary directory and clean it afterwards
    cmds:
      - mkdir -p tmp/task-demo
      - defer: rm -rf tmp/task-demo
      - echo "Doing work inside tmp/task-demo"</code></pre></div><p>The deferred command runs when the task finishes, including when a later command fails.</p><p>You can also defer another task:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;f4ed76f6-9f67-4602-9f30-84477b5b54c6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  demo:
    cmds:
      - mkdir -p tmp/task-demo
      - defer:
          task: cleanup
      - echo "Doing work"

  cleanup:
    internal: true
    cmds:
      - rm -rf tmp/task-demo</code></pre></div><p>This is useful for local integration tests, temporary fixtures and generated files.</p><h2>24. Output, silence and CI-friendly logs</h2><p>By default, Task prints command output in real time.</p><p>For simple local workflows, that is usually what you want.</p><p>For CI, parallel tasks can produce noisy logs. Task supports output modes:</p><ul><li><p><code>interleaved</code></p></li><li><p><code>group</code></p></li><li><p><code>prefixed</code><br>Example with grouped output:</p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;69114107-f0b9-44d8-9d82-15bb5f57b000&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

output:
  group:
    begin: '::group::{{.TASK}}'
    end: '::endgroup::'

tasks:
  verify:
    desc: Run checks with grouped logs
    cmds:
      - task: lint
      - task: test

  lint:
    cmds:
      - npm run lint

  test:
    cmds:
      - npm test</code></pre></div><p>Example with prefixed output:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;9773abbd-5f3b-41b5-b65d-525f20221219&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

output: prefixed

tasks:
  verify:
    deps:
      - task: lint
      - task: test

  lint:
    cmds:
      - npm run lint

  test:
    cmds:
      - npm test</code></pre></div><p>Silence controls whether Task echoes commands before running them.</p><p>At task level:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;ee980a9e-6189-4f52-b696-e4ca6144851e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  hello:
    silent: true
    cmds:
      - echo "Hello"</code></pre></div><p>At command level:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;c2e159c5-8c7f-4b58-858d-bdca5d44bcbc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  hello:
    cmds:
      - cmd: echo "Hello"
        silent: true</code></pre></div><p>At CLI level:</p><pre><code><code>task hello --silent</code></code></pre><p>Use silence with care. Hiding too much output can make debugging harder. A good default is to keep output visible for development tasks and tune CI output where logs become noisy.</p><h2>25. Watch mode</h2><p>Task can watch files and rerun a task when sources change.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;6aa169f6-8f4d-4c33-973e-99bfade8c5bb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

interval: 500ms

tasks:
  build:
    desc: Build when source files change
    watch: true
    sources:
      - package.json
      - 'src/**/*.js'
    cmds:
      - npm run build</code></pre></div><p>Run:</p><pre><code><code>task build</code></code></pre><p>or explicitly:</p><pre><code><code>task build --watch</code></code></pre><p>Watch mode requires <code>sources</code>, because Task needs to know what to observe.</p><p>Use watch mode for short, bounded tasks such as builds, generation and validation. Be cautious with long-running servers. For live-reload servers, a specialised tool may still be a better fit.</p><h2>26. Taskfile style guide</h2><p>A Taskfile should be pleasant to read.</p><p>The official style guide recommends simple conventions:</p><ul><li><p>Use the suggested section order</p></li><li><p>Use two spaces for indentation</p></li><li><p>Separate main sections with blank lines</p></li><li><p>Separate tasks with blank lines</p></li><li><p>Use uppercase variable names</p></li><li><p>Avoid whitespace inside template expressions</p></li><li><p>Use kebab case for task names</p></li><li><p>Use colons for namespaces</p></li><li><p>Prefer external scripts over complex multi-line commands<br>A clean Taskfile looks like this:</p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;7107bff0-4f00-4581-bcc5-89cbd72fa824&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  APP_NAME: checkout-api

tasks:
  test:
    desc: Run tests
    cmds:
      - npm test

  build:
    desc: Build the app
    cmds:
      - npm run build</code></pre></div><p>A messy Taskfile grows like this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;c547fc8f-4205-4232-b1c8-f25c535ba888&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'
tasks:
  test_everything_now:
    cmds:
      - npm test
  dockerBuild:
    cmds:
      - docker build -t app .</code></pre></div><p>The second one may work, but it communicates less.</p><p>Names are part of the interface. Treat them with care.</p><p>Good task names:</p><ul><li><p><code>setup</code></p></li><li><p><code>dev</code></p></li><li><p><code>test</code></p></li><li><p><code>test:unit</code></p></li><li><p><code>test:integration</code></p></li><li><p><code>verify</code></p></li><li><p><code>build</code></p></li><li><p><code>docker:build</code></p></li><li><p><code>docker:run</code></p></li><li><p><code>k8s:validate</code></p></li><li><p><code>k8s:apply</code></p></li><li><p><code>docs:serve</code></p></li><li><p><code>docs:check</code><br>Avoid names that encode implementation details too early:</p></li><li><p><code>run-npm-script-for-development</code></p></li><li><p><code>docker-build-new</code></p></li><li><p><code>fix-stuff</code></p></li><li><p><code>ci2</code></p></li><li><p><code>tmp-command</code><br>A task name should say what the developer wants, not expose every implementation step.</p></li></ul><h2>27. A complete practical single-file Taskfile</h2><p>At this point, the examples can become more complete.</p><p>Imagine a small Node.js service, such as an Express API used in a containerisation or Kubernetes course.</p><p>The project uses:</p><ul><li><p>npm</p></li><li><p>Docker</p></li><li><p>Docker Compose</p></li><li><p>Kubernetes manifests</p></li><li><p>jq</p></li><li><p>yq</p></li><li><p>1Password CLI for secure local development</p></li><li><p><code>.env.local</code> for local non-production configuration</p></li><li><p><code>.local-secrets</code> for local development secrets<br>A single-file Taskfile could look like this:</p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;96108262-c17e-4cd0-886f-32e3f85670a6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

output: prefixed

vars:
  APP_NAME: checkout-api
  IMAGE: checkout-api:local
  NAMESPACE: dev
  K8S_DIR: k8s
  LOCAL_SECRETS_DIR: .local-secrets
  DB_PASSWORD_FILE: '{{joinPath .LOCAL_SECRETS_DIR "db-password"}}'
  LOCAL_CERT_DIR: '{{joinPath .HOME ".certs" "checkout-api"}}'
  LOCAL_CERT_FILE: '{{joinPath .LOCAL_CERT_DIR "dev.crt"}}'

dotenv:
  - .env.local
  - .env
  - '{{.HOME}}/.checkout-api.env'

includes:
  local:
    taskfile: ./Taskfile.local.yml
    optional: true

tasks:
  default:
    desc: Show available tasks
    cmds:
      - task --list
    silent: true

  paths:info:
    desc: Print useful project and user paths
    cmds:
      - echo "Root directory {{.ROOT_DIR}}"
      - echo "Taskfile directory {{.TASKFILE_DIR}}"
      - echo "Task execution directory {{.TASK_DIR}}"
      - echo "User working directory {{.USER_WORKING_DIR}}"
      - echo "Local secrets directory {{.LOCAL_SECRETS_DIR}}"
      - echo "Local certificate file {{.LOCAL_CERT_FILE}}"

  setup:
    desc: Install project dependencies
    sources:
      - package.json
      - package-lock.json
    generates:
      - node_modules/.package-lock.json
    cmds:
      - npm ci

  dev:
    desc: Start the development server with local environment files
    deps: [setup]
    preconditions:
      - sh: test -f package.json
        msg: package.json is required
      - sh: test -f .env.local
        msg: Create .env.local from .env.example before running the app
    cmds:
      - npm run dev

  dev:secure:
    desc: Start the development server with secrets from 1Password
    deps: [setup]
    preconditions:
      - sh: command -v op
        msg: 1Password CLI is required
      - sh: test -f .env.1password
        msg: Create .env.1password with 1Password secret references
    cmds:
      - op run --env-file .env.1password -- npm run dev

  test:
    desc: Run the test suite
    deps: [setup]
    cmds:
      - npm test

  test:integration:
    desc: Run integration tests that require database credentials
    deps: [setup]
    requires:
      vars: [DB_USER, DB_PASSWORD]
    env:
      DB_USER: '{{.DB_USER}}'
      DB_PASSWORD: '{{.DB_PASSWORD}}'
    cmds:
      - npm run test:integration

  test:integration:secure:
    desc: Run integration tests with secrets from 1Password
    preconditions:
      - sh: command -v op
        msg: 1Password CLI is required
      - sh: test -f .env.1password
        msg: Create .env.1password with secret references
    cmds:
      - op run --env-file .env.1password -- task test:integration

  lint:
    desc: Run linting
    deps: [setup]
    cmds:
      - npm run lint

  build:
    desc: Build the service
    deps: [setup]
    sources:
      - package.json
      - package-lock.json
      - 'src/**/*.js'
    generates:
      - dist/server.js
    cmds:
      - npm run build

  verify:
    desc: Run local checks before pushing
    cmds:
      - task: lint
      - task: test
      - task: build
      - task: docker:build
      - task: k8s:validate

  secrets:init:
    desc: Create local development secret files
    cmds:
      - mkdir -p "{{.LOCAL_SECRETS_DIR}}"
      - test -f "{{.DB_PASSWORD_FILE}}" || printf "checkout-dev-password\n" &gt; "{{.DB_PASSWORD_FILE}}"
      - cmd: chmod 600 "{{.DB_PASSWORD_FILE}}"
        platforms: [linux, darwin]

  secrets:check:
    desc: Check required local secret files without printing values
    preconditions:
      - sh: test -f "{{.DB_PASSWORD_FILE}}"
        msg: Create {{.DB_PASSWORD_FILE}} first
    cmds:
      - echo "Required local secret files are present"

  cert:check:
    desc: Check that the local development certificate exists
    preconditions:
      - sh: test -f "{{.LOCAL_CERT_FILE}}"
        msg: Create {{.LOCAL_CERT_FILE}} before starting HTTPS locally
    cmds:
      - echo "Local certificate is present"

  docker:build:
    desc: Build the local Docker image
    sources:
      - Dockerfile
      - package.json
      - package-lock.json
      - 'src/**/*.js'
    cmds:
      - docker build -t "{{.IMAGE}}" .

  docker:build-private:
    desc: Build the Docker image using a private npm configuration as a BuildKit secret
    preconditions:
      - sh: test -f "{{.HOME}}/.npmrc"
        msg: ~/.npmrc is required for private package installation
    cmds:
      - docker build --secret id=npmrc,src="{{.HOME}}/.npmrc" -t "{{.IMAGE}}" .

  docker:run:
    desc: Run the Docker image locally
    deps: [docker:build]
    cmds:
      - docker run --rm -p 3000:3000 "{{.IMAGE}}"

  compose:up:
    desc: Start local infrastructure with Docker Compose secrets
    deps: [secrets:check]
    preconditions:
      - sh: command -v docker
        msg: Docker is required
      - sh: test -f compose.yml
        msg: compose.yml is required
    cmds:
      - docker compose up -d

  compose:down:
    desc: Stop local infrastructure
    cmds:
      - docker compose down

  k8s:validate:
    desc: Validate Kubernetes YAML with yq and kubectl client dry run
    preconditions:
      - sh: command -v yq
        msg: yq is required
      - sh: command -v kubectl
        msg: kubectl is required
      - sh: test -d "{{.K8S_DIR}}"
        msg: Kubernetes directory not found
    cmds:
      - yq eval '.' "{{.K8S_DIR}}"/*.yaml &gt; /dev/null
      - kubectl apply --dry-run=client -f "{{.K8S_DIR}}"

  k8s:validate-server:
    desc: Validate Kubernetes manifests against the current cluster API
    preconditions:
      - sh: command -v kubectl
        msg: kubectl is required
      - sh: test -d "{{.K8S_DIR}}"
        msg: Kubernetes directory not found
    cmds:
      - kubectl apply --dry-run=server -f "{{.K8S_DIR}}"

  k8s:secret:create:
    desc: Create or update the app Kubernetes Secret from local files
    deps: [secrets:check]
    preconditions:
      - sh: command -v kubectl
        msg: kubectl is required
    cmds:
      - |
        kubectl create secret generic "{{.APP_NAME}}-secrets" \
          --namespace "{{.NAMESPACE}}" \
          --from-file=db-password="{{.DB_PASSWORD_FILE}}" \
          --dry-run=client \
          -o yaml | kubectl apply -f -

  k8s:secret:create-from-1password:
    desc: Create or update the app Kubernetes Secret from 1Password
    preconditions:
      - sh: command -v op
        msg: 1Password CLI is required
      - sh: command -v kubectl
        msg: kubectl is required
      - sh: test -f .env.1password
        msg: Create .env.1password with secret references
    cmds:
      - |
        op run --env-file .env.1password -- sh -c '
          kubectl create secret generic "{{.APP_NAME}}-secrets" \
            --namespace "{{.NAMESPACE}}" \
            --from-literal=db-password="$DB_PASSWORD" \
            --dry-run=client \
            -o yaml | kubectl apply -f -
        '

  k8s:apply:
    desc: Apply Kubernetes manifests to a namespace
    requires:
      vars: [NAMESPACE]
    prompt:
      - Apply manifests to namespace {{.NAMESPACE}}?
    cmds:
      - kubectl create namespace "{{.NAMESPACE}}" --dry-run=client -o yaml | kubectl apply -f -
      - task: k8s:secret:create
      - kubectl apply -n "{{.NAMESPACE}}" -f "{{.K8S_DIR}}"

  k8s:pods:
    desc: List pods as JSON and extract useful fields with jq
    preconditions:
      - sh: command -v jq
        msg: jq is required
      - sh: command -v kubectl
        msg: kubectl is required
    cmds:
      - kubectl get pods -n "{{.NAMESPACE}}" -o json | jq '.items[] | {name: .metadata.name, phase: .status.phase}'

  k8s:logs:
    desc: Show logs for the app deployment
    cmds:
      - kubectl logs -n "{{.NAMESPACE}}" "deploy/{{.APP_NAME}}"

  k8s:delete:
    desc: Delete Kubernetes manifests from a namespace
    prompt:
      - Delete manifests from namespace {{.NAMESPACE}}?
    cmds:
      - kubectl delete -n "{{.NAMESPACE}}" -f "{{.K8S_DIR}}"</code></pre></div><p>And these support files complete the example.</p><p><code>.gitignore</code>:</p><pre><code><code>.task/
.env
.env.local
.env.*.local
.env.1password
.local-secrets/
secrets/
Taskfile.local.yml</code></code></pre><p><code>.env.example</code>:</p><pre><code><code>PORT=3000
DB_HOST=localhost
DB_PORT=5432
DB_USER=checkout
DB_PASSWORD=</code></code></pre><p><code>.env.1password.example</code>:</p><pre><code><code>DB_PASSWORD=op://vault/item/password
STRIPE_SECRET_KEY=op://vault/item/secret-key</code></code></pre><p><code>compose.yml</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;1131aaff-3583-4955-9ae0-adb9800d8ffd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: checkout
      POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
      POSTGRES_DB: checkout
    secrets:
      - postgres_password
    ports:
      - "5432:5432"

secrets:
  postgres_password:
    file: ./.local-secrets/db-password</code></pre></div><p>This Taskfile is useful because it gives the project a clear interface.</p><p>For onboarding:</p><pre><code><code>task setup
task dev</code></code></pre><p>For local verification:</p><pre><code><code>task verify</code></code></pre><p>For Docker:</p><pre><code><code>task docker:build
task docker:run</code></code></pre><p>For local infrastructure:</p><pre><code><code>task secrets:init
task compose:up</code></code></pre><p>For secure local development:</p><pre><code><code>task dev:secure</code></code></pre><p>For Kubernetes:</p><pre><code><code>task k8s:validate
task k8s:validate-server
task k8s:apply NAMESPACE=dev
task k8s:pods
task k8s:logs</code></code></pre><p>For user-specific paths:</p><pre><code><code>task paths:info
task cert:check</code></code></pre><p>For safety, dangerous tasks use prompts. Required tools are checked through preconditions. Repeated build work uses <code>sources</code> and <code>generates</code>. Kubernetes inspection uses <code>jq</code>. YAML validation uses <code>yq</code>. Secrets are loaded from local files or 1Password, not committed into the Taskfile.</p><p>This is the kind of Taskfile that improves DevExp because it reduces the number of things a developer must remember before they can be useful.</p><h2>28. A complete modular Taskfile structure</h2><p>The previous example is useful because everything is in one place.</p><p>But once a Taskfile grows, one place becomes too much place.</p><p>The next step is to split by responsibility.</p><pre><code><code>.
&#9500;&#9472;&#9472; Taskfile.yml
&#9500;&#9472;&#9472; taskfiles
&#9474;   &#9500;&#9472;&#9472; Dev.yml
&#9474;   &#9500;&#9472;&#9472; Quality.yml
&#9474;   &#9500;&#9472;&#9472; Docker.yml
&#9474;   &#9500;&#9472;&#9472; Kubernetes.yml
&#9474;   &#9500;&#9472;&#9472; Secrets.yml
&#9474;   &#9492;&#9472;&#9472; Docs.yml
&#9500;&#9472;&#9472; scripts
&#9474;   &#9492;&#9472;&#9472; init-local-secrets.js
&#9500;&#9472;&#9472; package.json
&#9500;&#9472;&#9472; package-lock.json
&#9500;&#9472;&#9472; Dockerfile
&#9500;&#9472;&#9472; compose.yml
&#9500;&#9472;&#9472; .env.example
&#9500;&#9472;&#9472; .env.1password.example
&#9500;&#9472;&#9472; .gitignore
&#9492;&#9472;&#9472; k8s
    &#9500;&#9472;&#9472; deployment.yaml
    &#9492;&#9472;&#9472; service.yaml
</code></code></pre><p>The root Taskfile becomes the project&#8217;s front door.</p><h3>Taskfile.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;9793e88d-1948-4200-80dc-4dc2d595b865&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

output: prefixed

vars:
  APP_NAME: checkout-api
  IMAGE: checkout-api:local
  NAMESPACE: dev
  K8S_DIR: k8s
  LOCAL_SECRETS_DIR: .local-secrets
  DB_PASSWORD_FILE: '{{joinPath .LOCAL_SECRETS_DIR "db-password"}}'

dotenv:
  - .env.local
  - .env
  - '{{.HOME}}/.checkout-api.env'

includes:
  dev:
    taskfile: ./taskfiles/Dev.yml
  quality:
    taskfile: ./taskfiles/Quality.yml
  docker:
    taskfile: ./taskfiles/Docker.yml
    vars:
      IMAGE: '{{.IMAGE}}'
  k8s:
    taskfile: ./taskfiles/Kubernetes.yml
    vars:
      APP_NAME: '{{.APP_NAME}}'
      NAMESPACE: '{{.NAMESPACE}}'
      K8S_DIR: '{{.K8S_DIR}}'
      DB_PASSWORD_FILE: '{{.DB_PASSWORD_FILE}}'
  secrets:
    taskfile: ./taskfiles/Secrets.yml
    vars:
      LOCAL_SECRETS_DIR: '{{.LOCAL_SECRETS_DIR}}'
      DB_PASSWORD_FILE: '{{.DB_PASSWORD_FILE}}'
  docs:
    taskfile: ./taskfiles/Docs.yml
  local:
    taskfile: ./Taskfile.local.yml
    optional: true

tasks:
  default:
    desc: Show available tasks
    cmds:
      - task --list
    silent: true

  setup:
    desc: Prepare the project for local development
    cmds:
      - task: dev:setup
      - task: secrets:init

  dev:
    desc: Start the local development environment
    cmds:
      - task: dev:start

  verify:
    desc: Run all local checks before pushing
    cmds:
      - task: quality:lint
      - task: quality:test
      - task: quality:build
      - task: docker:build
      - task: k8s:validate

  ci:
    desc: Run the verification workflow used by CI
    cmds:
      - task: verify</code></pre></div><h3>taskfiles/Dev.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;41ff664c-5ab1-43f9-a131-d695310d93fd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  setup:
    desc: Install project dependencies
    sources:
      - package.json
      - package-lock.json
    generates:
      - node_modules/.package-lock.json
    cmds:
      - npm ci

  start:
    desc: Start the development server
    deps: [setup]
    preconditions:
      - sh: test -f package.json
        msg: package.json is required
      - sh: test -f .env.local
        msg: Create .env.local from .env.example before starting the app
    cmds:
      - npm run dev

  secure:
    desc: Start the development server with secrets from 1Password
    deps: [setup]
    preconditions:
      - sh: command -v op
        msg: 1Password CLI is required
      - sh: test -f .env.1password
        msg: Create .env.1password with 1Password secret references
    cmds:
      - op run --env-file .env.1password -- npm run dev</code></pre></div><h3>taskfiles/Quality.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;3c7601af-db92-451b-b5c4-ed5de86a67f3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  lint:
    desc: Run linting
    cmds:
      - npm run lint

  test:
    desc: Run the test suite
    cmds:
      - npm test

  test-integration:
    desc: Run integration tests that require database credentials
    requires:
      vars: [DB_USER, DB_PASSWORD]
    env:
      DB_USER: '{{.DB_USER}}'
      DB_PASSWORD: '{{.DB_PASSWORD}}'
    cmds:
      - npm run test:integration

  test-integration-secure:
    desc: Run integration tests with secrets from 1Password
    preconditions:
      - sh: command -v op
        msg: 1Password CLI is required
      - sh: test -f .env.1password
        msg: Create .env.1password with secret references
    cmds:
      - op run --env-file .env.1password -- task quality:test-integration

  build:
    desc: Build the application
    sources:
      - package.json
      - package-lock.json
      - 'src/**/*.js'
    generates:
      - dist/server.js
    cmds:
      - npm run build</code></pre></div><h3>taskfiles/Docker.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;d713c201-d7f3-457c-9118-c5fe611ca61c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  IMAGE: checkout-api:local

tasks:
  build:
    desc: Build the local Docker image
    sources:
      - Dockerfile
      - package.json
      - package-lock.json
      - 'src/**/*.js'
    cmds:
      - docker build -t "{{.IMAGE}}" .

  build-private:
    desc: Build the Docker image using a private npm configuration as a BuildKit secret
    preconditions:
      - sh: test -f "{{.HOME}}/.npmrc"
        msg: ~/.npmrc is required for private package installation
    cmds:
      - docker build --secret id=npmrc,src="{{.HOME}}/.npmrc" -t "{{.IMAGE}}" .

  run:
    desc: Run the Docker image locally
    deps: [build]
    cmds:
      - docker run --rm -p 3000:3000 "{{.IMAGE}}"

  compose-up:
    desc: Start local infrastructure with Docker Compose
    cmds:
      - docker compose up -d

  compose-down:
    desc: Stop local infrastructure
    cmds:
      - docker compose down</code></pre></div><h3>taskfiles/Kubernetes.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;720a4ddf-b256-4e48-86d1-524232feed34&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  APP_NAME: checkout-api
  NAMESPACE: dev
  K8S_DIR: k8s
  DB_PASSWORD_FILE: .local-secrets/db-password

tasks:
  validate:
    desc: Validate Kubernetes manifests locally
    preconditions:
      - sh: command -v yq
        msg: yq is required
      - sh: command -v kubectl
        msg: kubectl is required
      - sh: test -d "{{.K8S_DIR}}"
        msg: Kubernetes directory not found
    cmds:
      - yq eval '.' "{{.K8S_DIR}}"/*.yaml &gt; /dev/null
      - kubectl apply --dry-run=client -f "{{.K8S_DIR}}"

  validate-server:
    desc: Validate Kubernetes manifests against the current cluster API
    preconditions:
      - sh: command -v kubectl
        msg: kubectl is required
      - sh: test -d "{{.K8S_DIR}}"
        msg: Kubernetes directory not found
    cmds:
      - kubectl apply --dry-run=server -f "{{.K8S_DIR}}"

  secret-create:
    desc: Create or update the app Kubernetes Secret from local files
    preconditions:
      - sh: command -v kubectl
        msg: kubectl is required
      - sh: test -f "{{.DB_PASSWORD_FILE}}"
        msg: Create {{.DB_PASSWORD_FILE}} first
    cmds:
      - |
        kubectl create secret generic "{{.APP_NAME}}-secrets" \
          --namespace "{{.NAMESPACE}}" \
          --from-file=db-password="{{.DB_PASSWORD_FILE}}" \
          --dry-run=client \
          -o yaml | kubectl apply -f -

  secret-create-from-1password:
    desc: Create or update the app Kubernetes Secret from 1Password
    preconditions:
      - sh: command -v op
        msg: 1Password CLI is required
      - sh: command -v kubectl
        msg: kubectl is required
      - sh: test -f .env.1password
        msg: Create .env.1password with secret references
    cmds:
      - |
        op run --env-file .env.1password -- sh -c '
          kubectl create secret generic "{{.APP_NAME}}-secrets" \
            --namespace "{{.NAMESPACE}}" \
            --from-literal=db-password="$DB_PASSWORD" \
            --dry-run=client \
            -o yaml | kubectl apply -f -
        '

  apply:
    desc: Apply Kubernetes manifests to a namespace
    prompt:
      - Apply manifests to namespace {{.NAMESPACE}}?
    cmds:
      - kubectl create namespace "{{.NAMESPACE}}" --dry-run=client -o yaml | kubectl apply -f -
      - task: secret-create
      - kubectl apply -n "{{.NAMESPACE}}" -f "{{.K8S_DIR}}"

  pods:
    desc: List pods as JSON and extract useful fields with jq
    preconditions:
      - sh: command -v jq
        msg: jq is required
      - sh: command -v kubectl
        msg: kubectl is required
    cmds:
      - kubectl get pods -n "{{.NAMESPACE}}" -o json | jq '.items[] | {name: .metadata.name, phase: .status.phase}'

  logs:
    desc: Show logs for the app deployment
    cmds:
      - kubectl logs -n "{{.NAMESPACE}}" "deploy/{{.APP_NAME}}"

  delete:
    desc: Delete Kubernetes manifests from a namespace
    prompt:
      - Delete manifests from namespace {{.NAMESPACE}}?
    cmds:
      - kubectl delete -n "{{.NAMESPACE}}" -f "{{.K8S_DIR}}"</code></pre></div><h3>taskfiles/Secrets.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;3a03bd7d-c507-46da-a842-2183dd1a9819&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  LOCAL_SECRETS_DIR: .local-secrets
  DB_PASSWORD_FILE: '{{joinPath .LOCAL_SECRETS_DIR "db-password"}}'

tasks:
  init:
    desc: Create local development secret files
    cmds:
      - mkdir -p "{{.LOCAL_SECRETS_DIR}}"
      - test -f "{{.DB_PASSWORD_FILE}}" || printf "checkout-dev-password\n" &gt; "{{.DB_PASSWORD_FILE}}"
      - cmd: chmod 600 "{{.DB_PASSWORD_FILE}}"
        platforms: [linux, darwin]

  init-portable:
    desc: Create local development secret files with Node.js
    cmds:
      - node scripts/init-local-secrets.js

  check:
    desc: Check required local secret files without printing values
    preconditions:
      - sh: test -f "{{.DB_PASSWORD_FILE}}"
        msg: Create {{.DB_PASSWORD_FILE}} first
    cmds:
      - echo "Required local secret files are present"

  check-env:
    desc: Check required secret variables without printing values
    cmds:
      - test -n "$DB_PASSWORD"
      - echo "Required secret variables are present"</code></pre></div><h3>taskfiles/Docs.yml</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;10c5734b-d48a-4044-acc3-1499acf9a4e3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  serve:
    desc: Serve documentation locally
    cmds:
      - npm run docs:serve

  check:
    desc: Check documentation quality
    cmds:
      - npm run docs:check</code></pre></div><h3>Taskfile.local.yml</h3><p>This file is optional and ignored by Git.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;9ca37071-f822-4357-94b0-4f708589a553&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  tunnel:
    desc: Start my personal local tunnel
    cmds:
      - cloudflared tunnel run checkout-api-dev

  open-db:
    desc: Open my local database client
    cmds:
      - open -a TablePlus</code></pre></div><p>Add it to <code>.gitignore</code>:</p><pre><code><code>Taskfile.local.yml</code></code></pre><p>This modular structure is more advanced, but it is also easier to scale.</p><p>The root file remains the narrative entry point.</p><p>The included files hold responsibility-specific detail.</p><p>The team still gets a single command interface:</p><pre><code><code>task setup
task dev
task verify
task docker:build
task docker:compose-up
task k8s:validate
task k8s:validate-server
task k8s:apply
task secrets:check
task docs:serve</code></code></pre><p>That is the balance to protect.</p><p>Do not turn the Taskfile structure into architecture theatre. Split when it helps people work with the project.</p><h2>29. Common mistakes</h2><h3>Treating Task as a bag of aliases</h3><p>A Taskfile should not be a random list of personal shortcuts.</p><p>Bad:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;ffa47e0f-5684-47b9-9094-34e962ec83e6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  thing:
    cmds:
      - npm run x

  stuff:
    cmds:
      - docker ps

  magic:
    cmds:
      - ./scripts/do-everything.sh</code></pre></div><p>Better:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;45cf23bb-fb5a-4187-bfb8-ede24fefbc29&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  setup:
    desc: Install dependencies
    cmds:
      - npm ci

  dev:
    desc: Start the development environment
    cmds:
      - npm run dev

  verify:
    desc: Run checks before pushing
    cmds:
      - task: test
      - task: build</code></pre></div><p>The difference is intent.</p><h3>Using deps for ordered workflows</h3><p>This is a frequent mistake:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;9739a53a-ba01-44ce-b30a-90c9482e73f3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  release:
    deps: [test, build, publish]</code></pre></div><p>Those dependencies can run in parallel. If order matters, write the order:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;b504a370-e13d-470d-baba-ce52e0e71fd0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  release:
    cmds:
      - task: test
      - task: build
      - task: publish</code></pre></div><h3>Hiding important tasks without descriptions</h3><p>If a task is part of the project interface, give it a description.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;4be4c164-1d08-4bf9-8e41-d76197e7fbe7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  verify:
    desc: Run local checks before pushing
    cmds:
      - task: test
      - task: build</code></pre></div><p>A task without a description may be fine for internal implementation detail. A public task should explain itself.</p><h3>Putting too much logic in YAML</h3><p>This is hard to maintain:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;80f358e4-5654-42fa-ab8e-b01485e41a0f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  complex:
    cmds:
      - |
        for file in $(find . -name '*.yaml'); do
          echo "$file"
          yq eval '.' "$file" &gt; /dev/null
        done</code></pre></div><p>This is usually better:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;de551958-56e2-4d5c-9e5d-0a2a9e228641&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  yaml:validate:
    desc: Validate YAML files
    cmds:
      - ./scripts/validate-yaml.sh</code></pre></div><p>Task should orchestrate. Scripts should hold complex logic.</p><h3>Ignoring local and CI drift</h3><p>If CI runs a different workflow from local development, Task loses part of its value.</p><p>A better pattern is:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;dd1b090a-b448-4e34-8550-e4f24f201580&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  ci:
    desc: Run the same verification used by CI
    cmds:
      - task: verify

  verify:
    desc: Run local verification
    cmds:
      - task: lint
      - task: test
      - task: build</code></pre></div><p>Then CI calls:</p><pre><code><code>task ci</code></code></pre><p>The closer local and CI workflows are, the fewer surprises the team gets.</p><h3>Hardcoding personal paths</h3><p>This is a common DevExp smell:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;d1a8c0f8-194c-4365-8aee-e58f711dd2a1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  config:
    cmds:
      - cat /Users/alice/dev/config.yml</code></pre></div><p>Better:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;98835444-a60d-4fce-97ef-3745858f0f6d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  CONFIG_FILE: '{{joinPath .HOME ".config" "checkout-api" "config.yml"}}'

tasks:
  config:
    desc: Show the configured local file path
    cmds:
      - echo "{{.CONFIG_FILE}}"</code></pre></div><p>Even better, fail clearly when the file matters:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;bc0bb95a-cbf8-45de-8e29-a11ebcd6b38d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

vars:
  CONFIG_FILE: '{{joinPath .HOME ".config" "checkout-api" "config.yml"}}'

tasks:
  config:check:
    desc: Check that the local config file exists
    preconditions:
      - sh: test -f "{{.CONFIG_FILE}}"
        msg: Missing local config file at {{.CONFIG_FILE}}
    cmds:
      - echo "Local config file exists"</code></pre></div><p>The task no longer assumes one developer&#8217;s machine.</p><h3>Assuming cross-platform means every command is portable</h3><p>Task can run on multiple platforms.</p><p>That does not make every shell command portable.</p><p>This is fragile if the team includes native Windows users:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;005aff9a-d1bd-4dcc-959c-be74c3ee363e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  clean:
    cmds:
      - rm -rf dist</code></pre></div><p>A more honest version is this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;eb03989f-20c9-450e-8b86-32e9b00ad70f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  clean:
    cmds:
      - cmd: rm -rf dist
        platforms: [linux, darwin]
      - cmd: powershell -Command "Remove-Item -Recurse -Force dist"
        platforms: [windows]</code></pre></div><p>Or move the operation to a cross-platform script if the project already has a runtime for it.</p><h3>Printing secrets to debug tasks</h3><p>This is not acceptable:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;ce258b7b-b3d9-4c27-8929-f16f5f5a4a3a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">version: '3'

tasks:
  debug:
    cmds:
      - echo "$DB_PASSWORD"</code></pre></div><p>Use presence checks instead:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;9c9f1cf1-35fe-46fd-81a7-59398417416e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  secrets:check:
    desc: Check required secret variables without printing values
    cmds:
      - test -n "$DB_PASSWORD"
      - echo "DB_PASSWORD is present"</code></pre></div><p>The difference is small in code and large in risk.</p><h3>Treating Kubernetes Secrets as enough</h3><p>A Kubernetes Secret is not a full security strategy by itself.</p><p>It is a Kubernetes object for holding sensitive values. You still need to think about encryption at rest, RBAC, namespace boundaries, auditability, rotation, backup exposure and how values reach the cluster in the first place.</p><p>Task can help you make the workflow repeatable.</p><p>It cannot decide your security model.</p><h3>Splitting Taskfiles too early</h3><p>This can look organised while making the project harder to understand:</p><pre><code><code>taskfiles/Build.yml
taskfiles/Test.yml
taskfiles/Dev.yml
taskfiles/Clean.yml
taskfiles/Utils.yml</code></code></pre><p>If each file contains one tiny task, the split probably adds more friction than value.</p><p>Start with one Taskfile.</p><p>Split when responsibilities become visible.</p><p>Do not use modularity to avoid naming things clearly.</p><h2>30. How to adopt Task in an existing project</h2><p>Start small.</p><p>Do not migrate every command on day one.</p><p>A good first Taskfile has only the core workflow:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;6053bdcf-d2c9-4995-86d3-2313bc895338&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: '3'

tasks:
  default:
    desc: Show available tasks
    cmds:
      - task --list
    silent: true

  setup:
    desc: Install dependencies
    cmds:
      - npm ci

  dev:
    desc: Start development
    cmds:
      - npm run dev

  test:
    desc: Run tests
    cmds:
      - npm test

  verify:
    desc: Run checks before pushing
    cmds:
      - task: test</code></pre></div><p>Then add tasks when the project feels friction.</p><p>Good candidates:</p><ul><li><p>A command people frequently ask about</p></li><li><p>A command that differs across operating systems</p></li><li><p>A command used both locally and in CI</p></li><li><p>A command that requires several flags</p></li><li><p>A command that has safety implications</p></li><li><p>A command that validates generated files, Kubernetes manifests, JSON or YAML</p></li><li><p>A command that new developers need during onboarding</p></li><li><p>A command that needs user-specific configuration</p></li><li><p>A command that needs secrets but should not expose them<br>A practical adoption path:</p></li></ul><ol><li><p>Add <code>task setup</code>, <code>task dev</code>, <code>task test</code> and <code>task verify</code></p></li><li><p>Add descriptions to make <code>task --list</code> useful</p></li><li><p>Move CI to call <code>task verify</code> or <code>task ci</code></p></li><li><p>Add Docker and Kubernetes tasks if the project uses containers</p></li><li><p>Add preconditions for required tools and files</p></li><li><p>Add <code>sources</code>, <code>generates</code> or <code>status</code> for expensive repeated work</p></li><li><p>Add user-specific path handling where developers currently copy local paths into docs</p></li><li><p>Add explicit secret workflows using <code>.env.example</code>, ignored local files and approved secret managers</p></li><li><p>Review which commands are truly cross-platform and which ones need <code>platforms</code> or scripts</p></li><li><p>Split into included Taskfiles only when the root Taskfile becomes difficult to navigate</p></li><li><p>Review the Taskfile as part of project maintenance<br>This last point matters. A Taskfile can rot like any other artifact. If it no longer reflects how the project works, it becomes another source of confusion.</p></li></ol><p>Treat it as production DevExp.</p><h2>31. Conclusions</h2><p>Task is valuable because it gives a project a shared interface.</p><p>That sounds small. It is not.</p><p>A shared interface changes how developers experience the project. They no longer need to reconstruct workflows from scattered documentation, shell history, CI configuration and team memory. They can ask the project what it knows how to do.</p><p>That reduces onboarding cost. It reduces repeated explanations. It reduces local and CI drift. It makes cross-platform work more explicit. It makes dangerous tasks more visible. It makes common work easier to repeat.</p><p>The value becomes even clearer when paths and secrets enter the picture.</p><p>Hardcoded paths turn one developer&#8217;s machine into an invisible dependency. Secrets inside automation turn convenience into risk. A good Taskfile avoids both problems. It lets the project define the workflow while letting each developer provide their own local configuration safely.</p><p>The same is true for modularity.</p><p>A small Taskfile can be simple and useful. A larger Taskfile can be split by responsibility. But the goal is not to create more files. The goal is to preserve one coherent project interface as the workflow grows.</p><p>The same is also true for cross-platform work.</p><p>Task can give the team a portable entry point. It cannot magically make every command portable. That is why good Taskfiles are explicit about platform-specific commands, and why complex logic often belongs in scripts written in runtimes the project already uses.</p><p>Task is not magic. A bad Taskfile is just another messy file. But a good Taskfile becomes a compact map of how to work with the system.</p><p>The best Taskfiles are boring in the right way.</p><p>They have clear names. They have descriptions. They separate public tasks from internal tasks. They use dependencies only when parallelism makes sense. They use sequential task calls when order matters. They check preconditions. They make repeated work incremental. They keep complex logic in scripts. They make local workflows and CI workflows closer. They do not hardcode personal paths. They do not store secrets. They do not pretend Kubernetes Secrets solve the whole security problem. They split only when the split makes the project easier to understand.</p><p>That is why Task is a strong DevExp tool.</p><p>Not because it saves a few keystrokes.</p><p>Because it turns operational knowledge into something the whole team can run.</p><h2>References</h2><ul><li><p>Task Project. <em>Task: The Modern Task Runner</em>. </p></li></ul><p>https://taskfile.dev/</p><ul><li><p>Task Project. <em>Installation</em>. <a href="https://taskfile.dev/docs/installation">https://taskfile.dev/docs/installation</a></p></li><li><p>Task Project. <em>Getting Started</em>. <a href="https://taskfile.dev/docs/getting-started">https://taskfile.dev/docs/getting-started</a></p></li><li><p>Task Project. <em>Guide</em>. <a href="https://taskfile.dev/docs/guide">https://taskfile.dev/docs/guide</a></p></li><li><p>Task Project. <em>Taskfile Schema Reference</em>. <a href="https://taskfile.dev/docs/reference/schema">https://taskfile.dev/docs/reference/schema</a></p></li><li><p>Task Project. <em>Command Line Interface Reference</em>. <a href="https://taskfile.dev/docs/reference/cli">https://taskfile.dev/docs/reference/cli</a></p></li><li><p>Task Project. <em>Environment Reference</em>. <a href="https://taskfile.dev/docs/reference/environment">https://taskfile.dev/docs/reference/environment</a></p></li><li><p>Task Project. <em>Configuration Reference</em>. <a href="https://taskfile.dev/docs/reference/config">https://taskfile.dev/docs/reference/config</a></p></li><li><p>Task Project. <em>Templating Reference</em>. <a href="https://taskfile.dev/docs/reference/templating">https://taskfile.dev/docs/reference/templating</a></p></li><li><p>Task Project. <em>Taskfile Versions</em>. <a href="https://taskfile.dev/docs/taskfile-versions">https://taskfile.dev/docs/taskfile-versions</a></p></li><li><p>Task Project. <em>Style Guide</em>. <a href="https://taskfile.dev/docs/styleguide">https://taskfile.dev/docs/styleguide</a></p></li><li><p>1Password. <em>run</em>. <a href="https://developer.1password.com/docs/cli/reference/commands/run/">https://developer.1password.com/docs/cli/reference/commands/run/</a></p></li><li><p>1Password. <em>Load secrets into the environment</em>. <a href="https://developer.1password.com/docs/cli/secrets-environment-variables/">https://developer.1password.com/docs/cli/secrets-environment-variables/</a></p></li><li><p>Docker. <em>Manage secrets securely in Docker Compose</em>. <a href="https://docs.docker.com/compose/how-tos/use-secrets/">https://docs.docker.com/compose/how-tos/use-secrets/</a></p></li><li><p>Docker. <em>Secrets</em>. <a href="https://docs.docker.com/reference/compose-file/secrets/">https://docs.docker.com/reference/compose-file/secrets/</a></p></li><li><p>Docker. <em>Build secrets</em>. <a href="https://docs.docker.com/build/building/secrets/">https://docs.docker.com/build/building/secrets/</a></p></li><li><p>Kubernetes. <em>Secrets</em>. <a href="https://kubernetes.io/docs/concepts/configuration/secret/">https://kubernetes.io/docs/concepts/configuration/secret/</a></p></li><li><p>Kubernetes. <em>kubectl create secret generic</em>. <a href="https://kubernetes.io/docs/reference/kubectl/generated/kubectl_create/kubectl_create_secret_generic/">https://kubernetes.io/docs/reference/kubectl/generated/kubectl_create/kubectl_create_secret_generic/</a></p></li><li><p>Kubernetes. <em>Managing Secrets using kubectl</em>. <a href="https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-kubectl/">https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-kubectl/</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Kata 1: Conference scheduler]]></title><description><![CDATA[The first of three katas to practice Object design]]></description><link>https://emmanuelvalverderamos.substack.com/p/kata-1-conference-scheduler</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/kata-1-conference-scheduler</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 21 Jul 2026 06:01:35 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!opEl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Training intent</h2><p>Practise discovering responsibilities and collaborations while implementing a small scheduling workflow.</p><p>The exercise is intentionally manual. The organiser already knows which talk they want to schedule, which room they want to use, and which time they want. The system accepts or rejects that request according to the business rules.</p><p>This kata is not about finding an optimal schedule. It is about noticing how responsibility placement changes as more scheduling rules appear.</p><h2>Business context</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!opEl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!opEl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!opEl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!opEl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!opEl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!opEl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:296263,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.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_!opEl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!opEl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!opEl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!opEl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb20910c5-7f6a-4fc8-a035-cc91691365a1_1024x559.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A one-day software conference needs a simple scheduling tool.</p><p>The organiser builds the public agenda by placing talks into rooms at specific times. Attendees use that agenda to know what happens, where it happens, and when it happens.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KQ34!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F919c0960-c966-4694-bf64-c13939ecbcda_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KQ34!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F919c0960-c966-4694-bf64-c13939ecbcda_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!KQ34!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F919c0960-c966-4694-bf64-c13939ecbcda_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!KQ34!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F919c0960-c966-4694-bf64-c13939ecbcda_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!KQ34!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F919c0960-c966-4694-bf64-c13939ecbcda_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KQ34!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F919c0960-c966-4694-bf64-c13939ecbcda_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/919c0960-c966-4694-bf64-c13939ecbcda_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:457729,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F919c0960-c966-4694-bf64-c13939ecbcda_1024x559.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_!KQ34!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F919c0960-c966-4694-bf64-c13939ecbcda_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!KQ34!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F919c0960-c966-4694-bf64-c13939ecbcda_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!KQ34!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F919c0960-c966-4694-bf64-c13939ecbcda_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!KQ34!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F919c0960-c966-4694-bf64-c13939ecbcda_1024x559.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The system must protect the agenda from impossible schedules. A conference schedule is not valid just because talks have titles and times. It must also respect rooms, speakers, availability, and suitability.</p><h2>Problem to solve</h2><p>Build the behaviour needed to schedule talks into rooms and show the public agenda.</p><p>A talk has:</p><ul><li><p>A title</p></li><li><p>A speaker</p></li><li><p>An expected number of attendees</p></li><li><p>Optional requirements, such as needing a projector</p></li></ul><p>A room has:</p><ul><li><p>A name</p></li><li><p>A capacity</p></li><li><p>Optional capabilities, such as having a projector</p></li></ul><p>A speaker may be unavailable at specific times.</p><h2>Vocabulary</h2><p>Use this vocabulary to describe the business problem. It is not a proposed implementation model.</p><ul><li><p>Conference</p></li><li><p>Public agenda</p></li><li><p>Talk</p></li><li><p>Speaker</p></li><li><p>Room</p></li><li><p>Time</p></li><li><p>Capacity</p></li><li><p>Capability</p></li><li><p>Scheduling request</p></li><li><p>Move request</p></li><li><p>Rejected request</p></li></ul><h2>Fixed rules</h2><ul><li><p>The organiser schedules one talk at a time.</p></li><li><p>Time is represented using fixed labels such as <code>09:00</code>, <code>10:00</code>, and <code>11:00</code>.</p></li><li><p>A room can host at most one talk at the same time.</p></li><li><p>A speaker can give at most one talk at the same time.</p></li><li><p>A room is suitable only when it has enough capacity and the required capabilities.</p></li><li><p>A rejected scheduling request must not change the existing agenda.</p></li><li><p>A rejected move request must not change the existing agenda.</p></li></ul><h2>Scenario map</h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CYg6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F468b4f01-6638-4007-87cc-b69bf18cdf8b_3547x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CYg6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F468b4f01-6638-4007-87cc-b69bf18cdf8b_3547x140.png 424w, https://substackcdn.com/image/fetch/$s_!CYg6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F468b4f01-6638-4007-87cc-b69bf18cdf8b_3547x140.png 848w, https://substackcdn.com/image/fetch/$s_!CYg6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F468b4f01-6638-4007-87cc-b69bf18cdf8b_3547x140.png 1272w, https://substackcdn.com/image/fetch/$s_!CYg6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F468b4f01-6638-4007-87cc-b69bf18cdf8b_3547x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CYg6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F468b4f01-6638-4007-87cc-b69bf18cdf8b_3547x140.png" width="1456" height="57" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/468b4f01-6638-4007-87cc-b69bf18cdf8b_3547x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:57,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:35990,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F468b4f01-6638-4007-87cc-b69bf18cdf8b_3547x140.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_!CYg6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F468b4f01-6638-4007-87cc-b69bf18cdf8b_3547x140.png 424w, https://substackcdn.com/image/fetch/$s_!CYg6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F468b4f01-6638-4007-87cc-b69bf18cdf8b_3547x140.png 848w, https://substackcdn.com/image/fetch/$s_!CYg6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F468b4f01-6638-4007-87cc-b69bf18cdf8b_3547x140.png 1272w, https://substackcdn.com/image/fetch/$s_!CYg6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F468b4f01-6638-4007-87cc-b69bf18cdf8b_3547x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Participant checklist</h2><p>Before moving to the next scenario:</p><ul><li><p>Is the behaviour covered by a failing test first?</p></li><li><p>Is the expected outcome observable from outside the implementation?</p></li><li><p>Are previous scenarios still passing?</p></li><li><p>Does the test describe business behaviour rather than internal structure?</p></li><li><p>Did the last change introduce duplication worth refactoring?</p></li><li><p>Can the current design be explained without mentioning a framework, database, or user interface?</p></li></ul><div class="callout-block" data-callout="true"><p><strong>&#9888;&#65039; This kata has counterexample that you do not have to follow, they are meant to force you, to think about the sad path</strong></p></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Rule 1: The agenda reflects scheduled talks</h2><h3>What this rule means</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uAi8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81701a19-e504-4301-8115-184f05c0dd07_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uAi8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81701a19-e504-4301-8115-184f05c0dd07_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uAi8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81701a19-e504-4301-8115-184f05c0dd07_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uAi8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81701a19-e504-4301-8115-184f05c0dd07_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uAi8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81701a19-e504-4301-8115-184f05c0dd07_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uAi8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81701a19-e504-4301-8115-184f05c0dd07_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/81701a19-e504-4301-8115-184f05c0dd07_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:262705,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81701a19-e504-4301-8115-184f05c0dd07_1024x559.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_!uAi8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81701a19-e504-4301-8115-184f05c0dd07_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uAi8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81701a19-e504-4301-8115-184f05c0dd07_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uAi8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81701a19-e504-4301-8115-184f05c0dd07_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uAi8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81701a19-e504-4301-8115-184f05c0dd07_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The public agenda is the visible result of the scheduling work.</p><p>If nothing has been scheduled, the agenda is empty. If talks have been scheduled, the agenda shows those talks. The agenda should be useful for attendees, so talks must be shown in chronological order, regardless of the order in which the organiser scheduled them.</p><p>This rule does not say how the schedule is stored. It only says what the conference must be able to show.</p><h3>Why this rule matters</h3><p>This rule gives the kata its first observable behaviour.</p><p>Before rejecting invalid schedules, checking room conflicts, or moving talks, the system must be able to show what has already been accepted into the conference schedule.</p><p>The agenda is not a technical report. It is what attendees would read to know what happens during the day.</p><h3>Example</h3><p>The organiser schedules &#8220;Refactoring legacy code&#8221; at <code>09:00</code>.</p><p>When the public agenda is requested, the talk appears at <code>09:00</code>.</p><h3>Counterexample</h3><p>A new conference has no scheduled talks.</p><p>When the public agenda is requested, it should not invent placeholder talks, default sessions, or sample data. It should be empty.</p><h3>What this rule adds to the kata</h3><p>This rule starts with visibility. The first behaviour is not validation. The first behaviour is showing what the business has accepted so far.</p><p>Later rules will make it harder for something to become accepted into the agenda. For now, the goal is simply to make accepted talks visible.</p><h3>Rule diagram</h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!y3er!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aadc773-7466-47ef-968e-ab80b1c39417_2751x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!y3er!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aadc773-7466-47ef-968e-ab80b1c39417_2751x188.png 424w, https://substackcdn.com/image/fetch/$s_!y3er!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aadc773-7466-47ef-968e-ab80b1c39417_2751x188.png 848w, https://substackcdn.com/image/fetch/$s_!y3er!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aadc773-7466-47ef-968e-ab80b1c39417_2751x188.png 1272w, https://substackcdn.com/image/fetch/$s_!y3er!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aadc773-7466-47ef-968e-ab80b1c39417_2751x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!y3er!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aadc773-7466-47ef-968e-ab80b1c39417_2751x188.png" width="1456" height="100" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5aadc773-7466-47ef-968e-ab80b1c39417_2751x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:100,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:39533,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aadc773-7466-47ef-968e-ab80b1c39417_2751x188.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_!y3er!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aadc773-7466-47ef-968e-ab80b1c39417_2751x188.png 424w, https://substackcdn.com/image/fetch/$s_!y3er!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aadc773-7466-47ef-968e-ab80b1c39417_2751x188.png 848w, https://substackcdn.com/image/fetch/$s_!y3er!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aadc773-7466-47ef-968e-ab80b1c39417_2751x188.png 1272w, https://substackcdn.com/image/fetch/$s_!y3er!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aadc773-7466-47ef-968e-ab80b1c39417_2751x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 1.1: Show an empty agenda</h3><h4>Business example</h4><p>A conference has just been created and no talk has been placed yet.</p><h4>Gherkin</h4><pre><code><code>Scenario: Show an empty agenda
  Given a conference with no scheduled talks
  When the public agenda is requested
  Then the agenda is empty</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!L5Ov!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cd99189-bfc4-48db-8122-a749fb476c8a_1419x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!L5Ov!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cd99189-bfc4-48db-8122-a749fb476c8a_1419x140.png 424w, https://substackcdn.com/image/fetch/$s_!L5Ov!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cd99189-bfc4-48db-8122-a749fb476c8a_1419x140.png 848w, https://substackcdn.com/image/fetch/$s_!L5Ov!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cd99189-bfc4-48db-8122-a749fb476c8a_1419x140.png 1272w, https://substackcdn.com/image/fetch/$s_!L5Ov!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cd99189-bfc4-48db-8122-a749fb476c8a_1419x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!L5Ov!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cd99189-bfc4-48db-8122-a749fb476c8a_1419x140.png" width="1419" height="140" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9cd99189-bfc4-48db-8122-a749fb476c8a_1419x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:140,&quot;width&quot;:1419,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:18425,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cd99189-bfc4-48db-8122-a749fb476c8a_1419x140.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_!L5Ov!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cd99189-bfc4-48db-8122-a749fb476c8a_1419x140.png 424w, https://substackcdn.com/image/fetch/$s_!L5Ov!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cd99189-bfc4-48db-8122-a749fb476c8a_1419x140.png 848w, https://substackcdn.com/image/fetch/$s_!L5Ov!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cd99189-bfc4-48db-8122-a749fb476c8a_1419x140.png 1272w, https://substackcdn.com/image/fetch/$s_!L5Ov!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cd99189-bfc4-48db-8122-a749fb476c8a_1419x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 1.2: Schedule one talk</h3><h4>Business example</h4><p>The organiser places one talk in a room that can host it.</p><h4>Gherkin</h4><pre><code><code>Scenario: Schedule one talk
  Given Room A can host "Refactoring legacy code" at 09:00
  When the organiser schedules "Refactoring legacy code" in Room A at 09:00
  Then "Refactoring legacy code" appears in the public agenda at 09:00</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_QRo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2caf0cc-05e3-463f-9389-73d941da7f84_1792x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_QRo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2caf0cc-05e3-463f-9389-73d941da7f84_1792x188.png 424w, https://substackcdn.com/image/fetch/$s_!_QRo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2caf0cc-05e3-463f-9389-73d941da7f84_1792x188.png 848w, https://substackcdn.com/image/fetch/$s_!_QRo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2caf0cc-05e3-463f-9389-73d941da7f84_1792x188.png 1272w, https://substackcdn.com/image/fetch/$s_!_QRo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2caf0cc-05e3-463f-9389-73d941da7f84_1792x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_QRo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2caf0cc-05e3-463f-9389-73d941da7f84_1792x188.png" width="1456" height="153" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c2caf0cc-05e3-463f-9389-73d941da7f84_1792x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:153,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:30265,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2caf0cc-05e3-463f-9389-73d941da7f84_1792x188.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_!_QRo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2caf0cc-05e3-463f-9389-73d941da7f84_1792x188.png 424w, https://substackcdn.com/image/fetch/$s_!_QRo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2caf0cc-05e3-463f-9389-73d941da7f84_1792x188.png 848w, https://substackcdn.com/image/fetch/$s_!_QRo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2caf0cc-05e3-463f-9389-73d941da7f84_1792x188.png 1272w, https://substackcdn.com/image/fetch/$s_!_QRo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2caf0cc-05e3-463f-9389-73d941da7f84_1792x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h4></h4><h3>Scenario 1.3: Show several scheduled talks</h3><h4>Business example</h4><p>The public agenda should show more than one scheduled talk.</p><h4>Gherkin</h4><pre><code><code>Scenario: Show several scheduled talks
  Given 3 talks are scheduled
  When the public agenda is requested
  Then the agenda shows 3 talks</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mknp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddfa7c8a-6e70-4ed4-a4bb-2fed7e0a5270_1648x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mknp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddfa7c8a-6e70-4ed4-a4bb-2fed7e0a5270_1648x140.png 424w, https://substackcdn.com/image/fetch/$s_!mknp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddfa7c8a-6e70-4ed4-a4bb-2fed7e0a5270_1648x140.png 848w, https://substackcdn.com/image/fetch/$s_!mknp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddfa7c8a-6e70-4ed4-a4bb-2fed7e0a5270_1648x140.png 1272w, https://substackcdn.com/image/fetch/$s_!mknp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddfa7c8a-6e70-4ed4-a4bb-2fed7e0a5270_1648x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mknp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddfa7c8a-6e70-4ed4-a4bb-2fed7e0a5270_1648x140.png" width="1456" height="124" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ddfa7c8a-6e70-4ed4-a4bb-2fed7e0a5270_1648x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:124,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19639,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddfa7c8a-6e70-4ed4-a4bb-2fed7e0a5270_1648x140.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_!mknp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddfa7c8a-6e70-4ed4-a4bb-2fed7e0a5270_1648x140.png 424w, https://substackcdn.com/image/fetch/$s_!mknp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddfa7c8a-6e70-4ed4-a4bb-2fed7e0a5270_1648x140.png 848w, https://substackcdn.com/image/fetch/$s_!mknp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddfa7c8a-6e70-4ed4-a4bb-2fed7e0a5270_1648x140.png 1272w, https://substackcdn.com/image/fetch/$s_!mknp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddfa7c8a-6e70-4ed4-a4bb-2fed7e0a5270_1648x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h4></h4><h3>Scenario 1.4: Show scheduled talks in chronological order</h3><h4>Business example</h4><p>Talks may be scheduled in any order, but attendees need to read the agenda by time.</p><h4>Gherkin</h4><pre><code><code>Scenario: Show scheduled talks in chronological order
  Given talks are scheduled at 11:00, 09:00, and 10:00
  When the public agenda is requested
  Then the talks are shown as 09:00, 10:00, and 11:00</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SsV_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F937c8de6-87ee-4ea2-b348-a8a349fe0ea8_1714x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SsV_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F937c8de6-87ee-4ea2-b348-a8a349fe0ea8_1714x188.png 424w, https://substackcdn.com/image/fetch/$s_!SsV_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F937c8de6-87ee-4ea2-b348-a8a349fe0ea8_1714x188.png 848w, https://substackcdn.com/image/fetch/$s_!SsV_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F937c8de6-87ee-4ea2-b348-a8a349fe0ea8_1714x188.png 1272w, https://substackcdn.com/image/fetch/$s_!SsV_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F937c8de6-87ee-4ea2-b348-a8a349fe0ea8_1714x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SsV_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F937c8de6-87ee-4ea2-b348-a8a349fe0ea8_1714x188.png" width="1456" height="160" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/937c8de6-87ee-4ea2-b348-a8a349fe0ea8_1714x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:160,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31090,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F937c8de6-87ee-4ea2-b348-a8a349fe0ea8_1714x188.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_!SsV_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F937c8de6-87ee-4ea2-b348-a8a349fe0ea8_1714x188.png 424w, https://substackcdn.com/image/fetch/$s_!SsV_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F937c8de6-87ee-4ea2-b348-a8a349fe0ea8_1714x188.png 848w, https://substackcdn.com/image/fetch/$s_!SsV_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F937c8de6-87ee-4ea2-b348-a8a349fe0ea8_1714x188.png 1272w, https://substackcdn.com/image/fetch/$s_!SsV_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F937c8de6-87ee-4ea2-b348-a8a349fe0ea8_1714x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Rule 2: A room cannot host conflicting talks</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YW8n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7953ad62-0fb4-4f0d-a872-0deb0a7af013_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YW8n!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7953ad62-0fb4-4f0d-a872-0deb0a7af013_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!YW8n!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7953ad62-0fb4-4f0d-a872-0deb0a7af013_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!YW8n!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7953ad62-0fb4-4f0d-a872-0deb0a7af013_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!YW8n!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7953ad62-0fb4-4f0d-a872-0deb0a7af013_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YW8n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7953ad62-0fb4-4f0d-a872-0deb0a7af013_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7953ad62-0fb4-4f0d-a872-0deb0a7af013_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:386444,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7953ad62-0fb4-4f0d-a872-0deb0a7af013_1024x559.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_!YW8n!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7953ad62-0fb4-4f0d-a872-0deb0a7af013_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!YW8n!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7953ad62-0fb4-4f0d-a872-0deb0a7af013_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!YW8n!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7953ad62-0fb4-4f0d-a872-0deb0a7af013_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!YW8n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7953ad62-0fb4-4f0d-a872-0deb0a7af013_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>What this rule means</h3><p>A room can only host one talk at a given time.</p><p>Two talks may happen at the same time in the conference, but not in the same room. This is about protecting a physical constraint. A room cannot contain two independent sessions at the same time.</p><h3>Why this rule matters</h3><p>The agenda must be realistic.</p><p>A schedule that places two talks in Room A at <code>09:00</code> is not a valid conference schedule, even if both talks are otherwise valid.</p><p>This rule introduces the difference between:</p><pre><code><code>A talk can exist.</code></code></pre><p>and:</p><pre><code><code>A talk can be placed in this room at this time.</code></code></pre><h3>Example</h3><p>Room A hosts &#8220;Refactoring legacy code&#8221; at <code>09:00</code>.</p><p>Room B hosts &#8220;Testing microservices&#8221; at <code>09:00</code>.</p><p>This is allowed because the talks use different rooms.</p><h3>Counterexample</h3><p>Room A already hosts &#8220;Refactoring legacy code&#8221; at <code>09:00</code>.</p><p>The organiser then tries to schedule &#8220;Testing microservices&#8221; in Room A at <code>09:00</code>.</p><p>The request must be rejected.</p><h3>What this rule adds to the kata</h3><p>The system now has to preserve a valid agenda, not only record requested talks.</p><p>A rejected request must not replace or remove the talk that was already scheduled. Rejection is not just a return value or an error. It also means the previous valid schedule remains valid.</p><h3>Rule diagram</h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vRBY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90cbbf27-cfa2-4eb9-9b19-4a92f957c33e_2408x588.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vRBY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90cbbf27-cfa2-4eb9-9b19-4a92f957c33e_2408x588.png 424w, https://substackcdn.com/image/fetch/$s_!vRBY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90cbbf27-cfa2-4eb9-9b19-4a92f957c33e_2408x588.png 848w, https://substackcdn.com/image/fetch/$s_!vRBY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90cbbf27-cfa2-4eb9-9b19-4a92f957c33e_2408x588.png 1272w, https://substackcdn.com/image/fetch/$s_!vRBY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90cbbf27-cfa2-4eb9-9b19-4a92f957c33e_2408x588.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vRBY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90cbbf27-cfa2-4eb9-9b19-4a92f957c33e_2408x588.png" width="1456" height="356" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/90cbbf27-cfa2-4eb9-9b19-4a92f957c33e_2408x588.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:356,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:72978,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90cbbf27-cfa2-4eb9-9b19-4a92f957c33e_2408x588.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_!vRBY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90cbbf27-cfa2-4eb9-9b19-4a92f957c33e_2408x588.png 424w, https://substackcdn.com/image/fetch/$s_!vRBY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90cbbf27-cfa2-4eb9-9b19-4a92f957c33e_2408x588.png 848w, https://substackcdn.com/image/fetch/$s_!vRBY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90cbbf27-cfa2-4eb9-9b19-4a92f957c33e_2408x588.png 1272w, https://substackcdn.com/image/fetch/$s_!vRBY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90cbbf27-cfa2-4eb9-9b19-4a92f957c33e_2408x588.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p><h3>Scenario 2.1: Schedule two talks in different rooms at the same time</h3><h4>Business example</h4><p>Two different rooms can host two talks at the same time.</p><h4>Gherkin</h4><pre><code><code>Scenario: Schedule two talks in different rooms at the same time
  Given Room A is available at 09:00
  And Room B is available at 09:00
  When two talks are scheduled at 09:00 in different rooms
  Then both talks appear in the public agenda</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!o1nS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0adc7362-cceb-4b35-825e-8aefa828cd9c_1693x444.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!o1nS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0adc7362-cceb-4b35-825e-8aefa828cd9c_1693x444.png 424w, https://substackcdn.com/image/fetch/$s_!o1nS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0adc7362-cceb-4b35-825e-8aefa828cd9c_1693x444.png 848w, https://substackcdn.com/image/fetch/$s_!o1nS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0adc7362-cceb-4b35-825e-8aefa828cd9c_1693x444.png 1272w, https://substackcdn.com/image/fetch/$s_!o1nS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0adc7362-cceb-4b35-825e-8aefa828cd9c_1693x444.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!o1nS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0adc7362-cceb-4b35-825e-8aefa828cd9c_1693x444.png" width="1456" height="382" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0adc7362-cceb-4b35-825e-8aefa828cd9c_1693x444.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:382,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:54534,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0adc7362-cceb-4b35-825e-8aefa828cd9c_1693x444.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_!o1nS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0adc7362-cceb-4b35-825e-8aefa828cd9c_1693x444.png 424w, https://substackcdn.com/image/fetch/$s_!o1nS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0adc7362-cceb-4b35-825e-8aefa828cd9c_1693x444.png 848w, https://substackcdn.com/image/fetch/$s_!o1nS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0adc7362-cceb-4b35-825e-8aefa828cd9c_1693x444.png 1272w, https://substackcdn.com/image/fetch/$s_!o1nS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0adc7362-cceb-4b35-825e-8aefa828cd9c_1693x444.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Scenario 2.2: Reject two talks in the same room at the same time</h3><h4>Business example</h4><p>A room already used at <code>09:00</code> cannot host another talk at <code>09:00</code>.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject two talks in the same room at the same time
  Given Room A already has a talk at 09:00
  When another talk is scheduled in Room A at 09:00
  Then the scheduling request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pIkM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19e605b-9932-4e98-aca4-10850c305d79_1669x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pIkM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19e605b-9932-4e98-aca4-10850c305d79_1669x188.png 424w, https://substackcdn.com/image/fetch/$s_!pIkM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19e605b-9932-4e98-aca4-10850c305d79_1669x188.png 848w, https://substackcdn.com/image/fetch/$s_!pIkM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19e605b-9932-4e98-aca4-10850c305d79_1669x188.png 1272w, https://substackcdn.com/image/fetch/$s_!pIkM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19e605b-9932-4e98-aca4-10850c305d79_1669x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pIkM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19e605b-9932-4e98-aca4-10850c305d79_1669x188.png" width="1456" height="164" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e19e605b-9932-4e98-aca4-10850c305d79_1669x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:164,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31003,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19e605b-9932-4e98-aca4-10850c305d79_1669x188.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_!pIkM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19e605b-9932-4e98-aca4-10850c305d79_1669x188.png 424w, https://substackcdn.com/image/fetch/$s_!pIkM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19e605b-9932-4e98-aca4-10850c305d79_1669x188.png 848w, https://substackcdn.com/image/fetch/$s_!pIkM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19e605b-9932-4e98-aca4-10850c305d79_1669x188.png 1272w, https://substackcdn.com/image/fetch/$s_!pIkM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19e605b-9932-4e98-aca4-10850c305d79_1669x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 2.3: Keep the original talk after a room conflict</h3><h4>Business example</h4><p>A rejected conflicting request must not remove or replace the original scheduled talk.</p><h4>Gherkin</h4><pre><code><code>Scenario: Keep the original talk after a room conflict
  Given "Refactoring legacy code" is scheduled in Room A at 09:00
  When "Testing microservices" is scheduled in Room A at 09:00
  Then "Refactoring legacy code" remains scheduled in Room A at 09:00</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZpHc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aa70c82-3f0d-485f-9dfb-9ddefb38af59_2355x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZpHc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aa70c82-3f0d-485f-9dfb-9ddefb38af59_2355x188.png 424w, https://substackcdn.com/image/fetch/$s_!ZpHc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aa70c82-3f0d-485f-9dfb-9ddefb38af59_2355x188.png 848w, https://substackcdn.com/image/fetch/$s_!ZpHc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aa70c82-3f0d-485f-9dfb-9ddefb38af59_2355x188.png 1272w, https://substackcdn.com/image/fetch/$s_!ZpHc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aa70c82-3f0d-485f-9dfb-9ddefb38af59_2355x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZpHc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aa70c82-3f0d-485f-9dfb-9ddefb38af59_2355x188.png" width="1456" height="116" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3aa70c82-3f0d-485f-9dfb-9ddefb38af59_2355x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:116,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41750,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aa70c82-3f0d-485f-9dfb-9ddefb38af59_2355x188.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_!ZpHc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aa70c82-3f0d-485f-9dfb-9ddefb38af59_2355x188.png 424w, https://substackcdn.com/image/fetch/$s_!ZpHc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aa70c82-3f0d-485f-9dfb-9ddefb38af59_2355x188.png 848w, https://substackcdn.com/image/fetch/$s_!ZpHc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aa70c82-3f0d-485f-9dfb-9ddefb38af59_2355x188.png 1272w, https://substackcdn.com/image/fetch/$s_!ZpHc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aa70c82-3f0d-485f-9dfb-9ddefb38af59_2355x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Rule 3: A speaker cannot give overlapping talks</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IRBd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ff2e7e-4dfa-49b3-95ef-29528a7ecf68_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IRBd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ff2e7e-4dfa-49b3-95ef-29528a7ecf68_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!IRBd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ff2e7e-4dfa-49b3-95ef-29528a7ecf68_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!IRBd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ff2e7e-4dfa-49b3-95ef-29528a7ecf68_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!IRBd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ff2e7e-4dfa-49b3-95ef-29528a7ecf68_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IRBd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ff2e7e-4dfa-49b3-95ef-29528a7ecf68_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4ff2e7e-4dfa-49b3-95ef-29528a7ecf68_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:479478,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ff2e7e-4dfa-49b3-95ef-29528a7ecf68_1024x559.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_!IRBd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ff2e7e-4dfa-49b3-95ef-29528a7ecf68_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!IRBd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ff2e7e-4dfa-49b3-95ef-29528a7ecf68_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!IRBd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ff2e7e-4dfa-49b3-95ef-29528a7ecf68_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!IRBd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4ff2e7e-4dfa-49b3-95ef-29528a7ecf68_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>What this rule means</h3><p>A speaker can only give one talk at a given time.</p><p>Even if two different rooms are available, the same person cannot speak in both rooms at once.</p><p>This rule is different from room conflict. A room conflict is about a place. A speaker conflict is about a person.</p><h3>Why this rule matters</h3><p>A schedule can look valid from the room perspective and still be impossible for a speaker.</p><p>The conference needs a schedule that works for both attendees and speakers. A schedule that asks Alice to give two talks at <code>09:00</code> is not valid, even when those talks are in different rooms.</p><h3>Example</h3><p>Alice gives a talk at <code>09:00</code>.</p><p>Bob gives another talk at <code>09:00</code>.</p><p>This is allowed because they are different speakers.</p><h3>Counterexample</h3><p>Alice is already giving a talk at <code>09:00</code>.</p><p>The organiser tries to schedule another talk by Alice at <code>09:00</code>.</p><p>The request must be rejected.</p><h3>What this rule adds to the kata</h3><p>The system now has more than one kind of conflict.</p><p>A talk can be rejected because of the room, because of the speaker, or because the speaker is unavailable. The kata starts to move from simple storage toward business rules that interact.</p><h3>Rule diagram</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cADp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa04a2d8e-043e-46e9-a014-7939d0ddce2c_1788x588.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cADp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa04a2d8e-043e-46e9-a014-7939d0ddce2c_1788x588.png 424w, https://substackcdn.com/image/fetch/$s_!cADp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa04a2d8e-043e-46e9-a014-7939d0ddce2c_1788x588.png 848w, https://substackcdn.com/image/fetch/$s_!cADp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa04a2d8e-043e-46e9-a014-7939d0ddce2c_1788x588.png 1272w, https://substackcdn.com/image/fetch/$s_!cADp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa04a2d8e-043e-46e9-a014-7939d0ddce2c_1788x588.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cADp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa04a2d8e-043e-46e9-a014-7939d0ddce2c_1788x588.png" width="1456" height="479" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a04a2d8e-043e-46e9-a014-7939d0ddce2c_1788x588.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:479,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:56354,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa04a2d8e-043e-46e9-a014-7939d0ddce2c_1788x588.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_!cADp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa04a2d8e-043e-46e9-a014-7939d0ddce2c_1788x588.png 424w, https://substackcdn.com/image/fetch/$s_!cADp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa04a2d8e-043e-46e9-a014-7939d0ddce2c_1788x588.png 848w, https://substackcdn.com/image/fetch/$s_!cADp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa04a2d8e-043e-46e9-a014-7939d0ddce2c_1788x588.png 1272w, https://substackcdn.com/image/fetch/$s_!cADp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa04a2d8e-043e-46e9-a014-7939d0ddce2c_1788x588.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Scenario 3.1: Schedule two talks by different speakers at the same time</h3><h4>Business example</h4><p>Different speakers can give different talks at the same time.</p><h4>Gherkin</h4><pre><code><code>Scenario: Schedule two talks by different speakers at the same time
  Given Alice is available at 09:00
  And Bob is available at 09:00
  When their talks are scheduled at 09:00
  Then both talks appear in the public agenda</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rJaO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c73c32-9d6a-4db1-b893-e963e6e144ca_1650x348.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rJaO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c73c32-9d6a-4db1-b893-e963e6e144ca_1650x348.png 424w, https://substackcdn.com/image/fetch/$s_!rJaO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c73c32-9d6a-4db1-b893-e963e6e144ca_1650x348.png 848w, https://substackcdn.com/image/fetch/$s_!rJaO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c73c32-9d6a-4db1-b893-e963e6e144ca_1650x348.png 1272w, https://substackcdn.com/image/fetch/$s_!rJaO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c73c32-9d6a-4db1-b893-e963e6e144ca_1650x348.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rJaO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c73c32-9d6a-4db1-b893-e963e6e144ca_1650x348.png" width="1456" height="307" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/60c73c32-9d6a-4db1-b893-e963e6e144ca_1650x348.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:307,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:44442,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c73c32-9d6a-4db1-b893-e963e6e144ca_1650x348.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_!rJaO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c73c32-9d6a-4db1-b893-e963e6e144ca_1650x348.png 424w, https://substackcdn.com/image/fetch/$s_!rJaO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c73c32-9d6a-4db1-b893-e963e6e144ca_1650x348.png 848w, https://substackcdn.com/image/fetch/$s_!rJaO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c73c32-9d6a-4db1-b893-e963e6e144ca_1650x348.png 1272w, https://substackcdn.com/image/fetch/$s_!rJaO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60c73c32-9d6a-4db1-b893-e963e6e144ca_1650x348.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 3.2: Reject two talks by the same speaker at the same time</h3><h4>Business example</h4><p>Alice cannot give two talks at <code>09:00</code>.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject two talks by the same speaker at the same time
  Given Alice is already speaking at 09:00
  When another talk by Alice is scheduled at 09:00
  Then the scheduling request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YQ_f!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F422c645d-d184-4942-bda2-783d72760897_1669x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YQ_f!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F422c645d-d184-4942-bda2-783d72760897_1669x188.png 424w, https://substackcdn.com/image/fetch/$s_!YQ_f!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F422c645d-d184-4942-bda2-783d72760897_1669x188.png 848w, https://substackcdn.com/image/fetch/$s_!YQ_f!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F422c645d-d184-4942-bda2-783d72760897_1669x188.png 1272w, https://substackcdn.com/image/fetch/$s_!YQ_f!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F422c645d-d184-4942-bda2-783d72760897_1669x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YQ_f!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F422c645d-d184-4942-bda2-783d72760897_1669x188.png" width="1456" height="164" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/422c645d-d184-4942-bda2-783d72760897_1669x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:164,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:32033,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F422c645d-d184-4942-bda2-783d72760897_1669x188.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_!YQ_f!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F422c645d-d184-4942-bda2-783d72760897_1669x188.png 424w, https://substackcdn.com/image/fetch/$s_!YQ_f!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F422c645d-d184-4942-bda2-783d72760897_1669x188.png 848w, https://substackcdn.com/image/fetch/$s_!YQ_f!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F422c645d-d184-4942-bda2-783d72760897_1669x188.png 1272w, https://substackcdn.com/image/fetch/$s_!YQ_f!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F422c645d-d184-4942-bda2-783d72760897_1669x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 3.3: Reject a talk when the speaker is unavailable</h3><h4>Business example</h4><p>A speaker may be unavailable even when they are not already scheduled for another talk.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject a talk when the speaker is unavailable
  Given Alice is unavailable at 09:00
  When Alice's talk is scheduled at 09:00
  Then the scheduling request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TUZF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63413122-0ee9-427e-ad74-a8d2cbb23caf_1642x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TUZF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63413122-0ee9-427e-ad74-a8d2cbb23caf_1642x188.png 424w, https://substackcdn.com/image/fetch/$s_!TUZF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63413122-0ee9-427e-ad74-a8d2cbb23caf_1642x188.png 848w, https://substackcdn.com/image/fetch/$s_!TUZF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63413122-0ee9-427e-ad74-a8d2cbb23caf_1642x188.png 1272w, https://substackcdn.com/image/fetch/$s_!TUZF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63413122-0ee9-427e-ad74-a8d2cbb23caf_1642x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TUZF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63413122-0ee9-427e-ad74-a8d2cbb23caf_1642x188.png" width="1456" height="167" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/63413122-0ee9-427e-ad74-a8d2cbb23caf_1642x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:167,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26585,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63413122-0ee9-427e-ad74-a8d2cbb23caf_1642x188.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_!TUZF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63413122-0ee9-427e-ad74-a8d2cbb23caf_1642x188.png 424w, https://substackcdn.com/image/fetch/$s_!TUZF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63413122-0ee9-427e-ad74-a8d2cbb23caf_1642x188.png 848w, https://substackcdn.com/image/fetch/$s_!TUZF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63413122-0ee9-427e-ad74-a8d2cbb23caf_1642x188.png 1272w, https://substackcdn.com/image/fetch/$s_!TUZF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63413122-0ee9-427e-ad74-a8d2cbb23caf_1642x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Rule 4: A room must be suitable for the talk</h2><h3>What this rule means</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qbSl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7680789b-1eed-493d-9b83-8aeefff80deb_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qbSl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7680789b-1eed-493d-9b83-8aeefff80deb_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!qbSl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7680789b-1eed-493d-9b83-8aeefff80deb_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!qbSl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7680789b-1eed-493d-9b83-8aeefff80deb_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!qbSl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7680789b-1eed-493d-9b83-8aeefff80deb_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qbSl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7680789b-1eed-493d-9b83-8aeefff80deb_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7680789b-1eed-493d-9b83-8aeefff80deb_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:430852,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7680789b-1eed-493d-9b83-8aeefff80deb_1024x559.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_!qbSl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7680789b-1eed-493d-9b83-8aeefff80deb_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!qbSl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7680789b-1eed-493d-9b83-8aeefff80deb_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!qbSl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7680789b-1eed-493d-9b83-8aeefff80deb_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!qbSl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7680789b-1eed-493d-9b83-8aeefff80deb_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A room being free is not enough. The room must also be appropriate for the talk.</p><p>A talk may need enough seats for expected attendees. A talk may also need a capability such as a projector. If the room does not satisfy those needs, the talk cannot be placed there.</p><h3>Why this rule matters</h3><p>A schedule should not only avoid conflicts. It should also make sense operationally.</p><p>A room with 30 seats cannot host a talk expecting 40 people. A talk that requires a projector cannot be placed in a room without one.</p><h3>Example</h3><p>A room has capacity 40.</p><p>A talk expects 40 attendees.</p><p>The room is suitable by capacity.</p><h3>Counterexample</h3><p>A room has capacity 39.</p><p>A talk expects 40 attendees.</p><p>The request must be rejected because the room is too small.</p><h3>What this rule adds to the kata</h3><p>The idea of &#8220;valid scheduling&#8221; becomes richer.</p><p>A request can now be rejected even when the room is free and the speaker is available. Availability and suitability are not the same thing.</p><h3>Rule diagram</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!haGs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02d1815d-46ef-452a-b270-2412a1379f7c_1622x524.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!haGs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02d1815d-46ef-452a-b270-2412a1379f7c_1622x524.png 424w, https://substackcdn.com/image/fetch/$s_!haGs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02d1815d-46ef-452a-b270-2412a1379f7c_1622x524.png 848w, https://substackcdn.com/image/fetch/$s_!haGs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02d1815d-46ef-452a-b270-2412a1379f7c_1622x524.png 1272w, https://substackcdn.com/image/fetch/$s_!haGs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02d1815d-46ef-452a-b270-2412a1379f7c_1622x524.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!haGs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02d1815d-46ef-452a-b270-2412a1379f7c_1622x524.png" width="1456" height="470" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/02d1815d-46ef-452a-b270-2412a1379f7c_1622x524.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:470,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:54083,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02d1815d-46ef-452a-b270-2412a1379f7c_1622x524.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_!haGs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02d1815d-46ef-452a-b270-2412a1379f7c_1622x524.png 424w, https://substackcdn.com/image/fetch/$s_!haGs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02d1815d-46ef-452a-b270-2412a1379f7c_1622x524.png 848w, https://substackcdn.com/image/fetch/$s_!haGs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02d1815d-46ef-452a-b270-2412a1379f7c_1622x524.png 1272w, https://substackcdn.com/image/fetch/$s_!haGs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02d1815d-46ef-452a-b270-2412a1379f7c_1622x524.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Scenario 4.1: Schedule a talk when the room has exactly enough capacity</h3><h4>Business example</h4><p>A room with capacity 40 can host a talk expecting 40 attendees.</p><h4>Gherkin</h4><pre><code><code>Scenario: Schedule a talk when the room has exactly enough capacity
  Given Room A has capacity 40
  When a talk for 40 attendees is scheduled in Room A
  Then the talk appears in the public agenda</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aVZC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee73bdb2-86f7-4125-b896-23059eb7069d_1575x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aVZC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee73bdb2-86f7-4125-b896-23059eb7069d_1575x140.png 424w, https://substackcdn.com/image/fetch/$s_!aVZC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee73bdb2-86f7-4125-b896-23059eb7069d_1575x140.png 848w, https://substackcdn.com/image/fetch/$s_!aVZC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee73bdb2-86f7-4125-b896-23059eb7069d_1575x140.png 1272w, https://substackcdn.com/image/fetch/$s_!aVZC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee73bdb2-86f7-4125-b896-23059eb7069d_1575x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aVZC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee73bdb2-86f7-4125-b896-23059eb7069d_1575x140.png" width="1456" height="129" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee73bdb2-86f7-4125-b896-23059eb7069d_1575x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:129,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19941,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee73bdb2-86f7-4125-b896-23059eb7069d_1575x140.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_!aVZC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee73bdb2-86f7-4125-b896-23059eb7069d_1575x140.png 424w, https://substackcdn.com/image/fetch/$s_!aVZC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee73bdb2-86f7-4125-b896-23059eb7069d_1575x140.png 848w, https://substackcdn.com/image/fetch/$s_!aVZC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee73bdb2-86f7-4125-b896-23059eb7069d_1575x140.png 1272w, https://substackcdn.com/image/fetch/$s_!aVZC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee73bdb2-86f7-4125-b896-23059eb7069d_1575x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 4.2: Reject a talk when the room is too small</h3><h4>Business example</h4><p>A room with capacity 39 cannot host a talk expecting 40 attendees.</p><p>** <em>Keep in mind that this is not probably how you would implemented in real life, because you may reject the attendee if you instead of the talk or have other kind of solution for this, this is only for the kata purpose</em>.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject a talk when the room is too small
  Given Room A has capacity 39
  When a talk for 40 attendees is scheduled in Room A
  Then the scheduling request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sirG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed7b8ec-c7d5-46e7-b3e0-6de4cc4d86e6_1525x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sirG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed7b8ec-c7d5-46e7-b3e0-6de4cc4d86e6_1525x140.png 424w, https://substackcdn.com/image/fetch/$s_!sirG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed7b8ec-c7d5-46e7-b3e0-6de4cc4d86e6_1525x140.png 848w, https://substackcdn.com/image/fetch/$s_!sirG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed7b8ec-c7d5-46e7-b3e0-6de4cc4d86e6_1525x140.png 1272w, https://substackcdn.com/image/fetch/$s_!sirG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed7b8ec-c7d5-46e7-b3e0-6de4cc4d86e6_1525x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sirG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed7b8ec-c7d5-46e7-b3e0-6de4cc4d86e6_1525x140.png" width="1456" height="134" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6ed7b8ec-c7d5-46e7-b3e0-6de4cc4d86e6_1525x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:134,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:20424,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed7b8ec-c7d5-46e7-b3e0-6de4cc4d86e6_1525x140.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_!sirG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed7b8ec-c7d5-46e7-b3e0-6de4cc4d86e6_1525x140.png 424w, https://substackcdn.com/image/fetch/$s_!sirG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed7b8ec-c7d5-46e7-b3e0-6de4cc4d86e6_1525x140.png 848w, https://substackcdn.com/image/fetch/$s_!sirG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed7b8ec-c7d5-46e7-b3e0-6de4cc4d86e6_1525x140.png 1272w, https://substackcdn.com/image/fetch/$s_!sirG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed7b8ec-c7d5-46e7-b3e0-6de4cc4d86e6_1525x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 4.3: Schedule a talk when the room has the required capability</h3><h4>Business example</h4><p>A talk requiring a projector can be scheduled in a room with a projector.</p><h4>Gherkin</h4><pre><code><code>Scenario: Schedule a talk when the room has the required capability
  Given Room A has a projector
  When a talk requiring a projector is scheduled in Room A
  Then the talk appears in the public agenda</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WVnt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f5b580-a02a-4adb-aa0e-f6bc859dd79a_1551x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WVnt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f5b580-a02a-4adb-aa0e-f6bc859dd79a_1551x140.png 424w, https://substackcdn.com/image/fetch/$s_!WVnt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f5b580-a02a-4adb-aa0e-f6bc859dd79a_1551x140.png 848w, https://substackcdn.com/image/fetch/$s_!WVnt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f5b580-a02a-4adb-aa0e-f6bc859dd79a_1551x140.png 1272w, https://substackcdn.com/image/fetch/$s_!WVnt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f5b580-a02a-4adb-aa0e-f6bc859dd79a_1551x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WVnt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f5b580-a02a-4adb-aa0e-f6bc859dd79a_1551x140.png" width="1456" height="131" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b6f5b580-a02a-4adb-aa0e-f6bc859dd79a_1551x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:131,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:18057,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f5b580-a02a-4adb-aa0e-f6bc859dd79a_1551x140.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_!WVnt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f5b580-a02a-4adb-aa0e-f6bc859dd79a_1551x140.png 424w, https://substackcdn.com/image/fetch/$s_!WVnt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f5b580-a02a-4adb-aa0e-f6bc859dd79a_1551x140.png 848w, https://substackcdn.com/image/fetch/$s_!WVnt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f5b580-a02a-4adb-aa0e-f6bc859dd79a_1551x140.png 1272w, https://substackcdn.com/image/fetch/$s_!WVnt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f5b580-a02a-4adb-aa0e-f6bc859dd79a_1551x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 4.4: Reject a talk when the room lacks the required capability</h3><h4>Business example</h4><p>A talk requiring a projector cannot be scheduled in a room without one.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject a talk when the room lacks the required capability
  Given Room A has no projector
  When a talk requiring a projector is scheduled in Room A
  Then the scheduling request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fwE4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2609fb37-fbff-4075-a417-8fb603c8ddc0_1546x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fwE4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2609fb37-fbff-4075-a417-8fb603c8ddc0_1546x140.png 424w, https://substackcdn.com/image/fetch/$s_!fwE4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2609fb37-fbff-4075-a417-8fb603c8ddc0_1546x140.png 848w, https://substackcdn.com/image/fetch/$s_!fwE4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2609fb37-fbff-4075-a417-8fb603c8ddc0_1546x140.png 1272w, https://substackcdn.com/image/fetch/$s_!fwE4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2609fb37-fbff-4075-a417-8fb603c8ddc0_1546x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fwE4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2609fb37-fbff-4075-a417-8fb603c8ddc0_1546x140.png" width="1456" height="132" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2609fb37-fbff-4075-a417-8fb603c8ddc0_1546x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:132,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:17964,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2609fb37-fbff-4075-a417-8fb603c8ddc0_1546x140.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_!fwE4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2609fb37-fbff-4075-a417-8fb603c8ddc0_1546x140.png 424w, https://substackcdn.com/image/fetch/$s_!fwE4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2609fb37-fbff-4075-a417-8fb603c8ddc0_1546x140.png 848w, https://substackcdn.com/image/fetch/$s_!fwE4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2609fb37-fbff-4075-a417-8fb603c8ddc0_1546x140.png 1272w, https://substackcdn.com/image/fetch/$s_!fwE4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2609fb37-fbff-4075-a417-8fb603c8ddc0_1546x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Rule 5: Scheduled talks can be moved safely</h2><h3>What this rule means</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!V3E1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41f45d95-c1f8-4af4-ae8a-76a004d4364b_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!V3E1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41f45d95-c1f8-4af4-ae8a-76a004d4364b_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!V3E1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41f45d95-c1f8-4af4-ae8a-76a004d4364b_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!V3E1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41f45d95-c1f8-4af4-ae8a-76a004d4364b_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!V3E1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41f45d95-c1f8-4af4-ae8a-76a004d4364b_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!V3E1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41f45d95-c1f8-4af4-ae8a-76a004d4364b_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/41f45d95-c1f8-4af4-ae8a-76a004d4364b_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:376264,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41f45d95-c1f8-4af4-ae8a-76a004d4364b_1024x559.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_!V3E1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41f45d95-c1f8-4af4-ae8a-76a004d4364b_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!V3E1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41f45d95-c1f8-4af4-ae8a-76a004d4364b_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!V3E1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41f45d95-c1f8-4af4-ae8a-76a004d4364b_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!V3E1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41f45d95-c1f8-4af4-ae8a-76a004d4364b_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>The organiser can move an already scheduled talk to another room or time, but the move must obey the same business rules as a new scheduling request.</p><p>If the target room and time are valid, the talk moves. If the target is invalid, the move is rejected and the original schedule remains unchanged.</p><h3>Why this rule matters</h3><p>Real schedules change.</p><p>A speaker may request another time, a room may become unavailable, or the organiser may want to improve the agenda.</p><p>But changing a schedule is risky. A failed move should not leave the agenda half-changed. The system must protect the current valid agenda while trying to apply the requested change.</p><h3>Example</h3><p>&#8220;Refactoring legacy code&#8221; is scheduled at <code>09:00</code>.</p><p>The organiser moves it to an available slot at <code>10:00</code>.</p><p>The agenda now shows it at <code>10:00</code>.</p><h3>Counterexample</h3><p>&#8220;Refactoring legacy code&#8221; is scheduled in Room A at <code>09:00</code>.</p><p>The organiser tries to move it to Room B at <code>10:00</code>, but Room B is already occupied at <code>10:00</code>.</p><p>The move is rejected and the talk remains in Room A at <code>09:00</code>.</p><h3>What this rule adds to the kata</h3><p>The kata now asks for safe change, not only initial scheduling.</p><p>The system must protect the current valid schedule when a requested change cannot be accepted.</p><h3>Rule diagram</h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hqUy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F952343f4-ef6d-4808-8fd9-6ad9ccbbcb4f_2690x407.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hqUy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F952343f4-ef6d-4808-8fd9-6ad9ccbbcb4f_2690x407.png 424w, https://substackcdn.com/image/fetch/$s_!hqUy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F952343f4-ef6d-4808-8fd9-6ad9ccbbcb4f_2690x407.png 848w, https://substackcdn.com/image/fetch/$s_!hqUy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F952343f4-ef6d-4808-8fd9-6ad9ccbbcb4f_2690x407.png 1272w, https://substackcdn.com/image/fetch/$s_!hqUy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F952343f4-ef6d-4808-8fd9-6ad9ccbbcb4f_2690x407.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hqUy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F952343f4-ef6d-4808-8fd9-6ad9ccbbcb4f_2690x407.png" width="1456" height="220" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/952343f4-ef6d-4808-8fd9-6ad9ccbbcb4f_2690x407.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:220,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:70001,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F952343f4-ef6d-4808-8fd9-6ad9ccbbcb4f_2690x407.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_!hqUy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F952343f4-ef6d-4808-8fd9-6ad9ccbbcb4f_2690x407.png 424w, https://substackcdn.com/image/fetch/$s_!hqUy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F952343f4-ef6d-4808-8fd9-6ad9ccbbcb4f_2690x407.png 848w, https://substackcdn.com/image/fetch/$s_!hqUy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F952343f4-ef6d-4808-8fd9-6ad9ccbbcb4f_2690x407.png 1272w, https://substackcdn.com/image/fetch/$s_!hqUy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F952343f4-ef6d-4808-8fd9-6ad9ccbbcb4f_2690x407.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 5.1: Move a talk to an available slot</h3><h4>Business example</h4><p>A scheduled talk can be moved to another valid time.</p><h4>Gherkin</h4><pre><code><code>Scenario: Move a talk to an available slot
  Given "Refactoring legacy code" is scheduled at 09:00
  When the organiser moves it to 10:00
  Then "Refactoring legacy code" appears in the public agenda at 10:00</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!scY1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c48e2fa-0720-4560-91f9-c7049712c929_1618x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!scY1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c48e2fa-0720-4560-91f9-c7049712c929_1618x140.png 424w, https://substackcdn.com/image/fetch/$s_!scY1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c48e2fa-0720-4560-91f9-c7049712c929_1618x140.png 848w, https://substackcdn.com/image/fetch/$s_!scY1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c48e2fa-0720-4560-91f9-c7049712c929_1618x140.png 1272w, https://substackcdn.com/image/fetch/$s_!scY1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c48e2fa-0720-4560-91f9-c7049712c929_1618x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!scY1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c48e2fa-0720-4560-91f9-c7049712c929_1618x140.png" width="1456" height="126" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c48e2fa-0720-4560-91f9-c7049712c929_1618x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:126,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:22241,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c48e2fa-0720-4560-91f9-c7049712c929_1618x140.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_!scY1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c48e2fa-0720-4560-91f9-c7049712c929_1618x140.png 424w, https://substackcdn.com/image/fetch/$s_!scY1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c48e2fa-0720-4560-91f9-c7049712c929_1618x140.png 848w, https://substackcdn.com/image/fetch/$s_!scY1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c48e2fa-0720-4560-91f9-c7049712c929_1618x140.png 1272w, https://substackcdn.com/image/fetch/$s_!scY1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c48e2fa-0720-4560-91f9-c7049712c929_1618x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 5.2: Release the original slot after moving a talk</h3><h4>Business example</h4><p>After a successful move, another talk can use the original room and time.</p><h4>Gherkin</h4><pre><code><code>Scenario: Release the original slot after moving a talk
  Given "Refactoring legacy code" is scheduled in Room A at 09:00
  When the organiser moves it to Room B at 10:00
  Then another talk can be scheduled in Room A at 09:00</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1uAs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F877d0141-e202-403f-91c3-9fa75cee4f5a_1757x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1uAs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F877d0141-e202-403f-91c3-9fa75cee4f5a_1757x188.png 424w, https://substackcdn.com/image/fetch/$s_!1uAs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F877d0141-e202-403f-91c3-9fa75cee4f5a_1757x188.png 848w, https://substackcdn.com/image/fetch/$s_!1uAs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F877d0141-e202-403f-91c3-9fa75cee4f5a_1757x188.png 1272w, https://substackcdn.com/image/fetch/$s_!1uAs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F877d0141-e202-403f-91c3-9fa75cee4f5a_1757x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1uAs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F877d0141-e202-403f-91c3-9fa75cee4f5a_1757x188.png" width="1456" height="156" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/877d0141-e202-403f-91c3-9fa75cee4f5a_1757x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:156,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:32025,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F877d0141-e202-403f-91c3-9fa75cee4f5a_1757x188.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_!1uAs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F877d0141-e202-403f-91c3-9fa75cee4f5a_1757x188.png 424w, https://substackcdn.com/image/fetch/$s_!1uAs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F877d0141-e202-403f-91c3-9fa75cee4f5a_1757x188.png 848w, https://substackcdn.com/image/fetch/$s_!1uAs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F877d0141-e202-403f-91c3-9fa75cee4f5a_1757x188.png 1272w, https://substackcdn.com/image/fetch/$s_!1uAs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F877d0141-e202-403f-91c3-9fa75cee4f5a_1757x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 5.3: Reject a move that creates a room conflict</h3><h4>Business example</h4><p>A talk cannot be moved into a room and time that are already occupied.</p><h4>Gherkin</h4><pre><code><code>Scenario: Reject a move that creates a room conflict
  Given Room A already has a talk at 10:00
  When another talk is moved to Room A at 10:00
  Then the move request is rejected</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!moCf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccb06a5c-fb0a-43dc-96a7-4c5304b66a86_1629x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!moCf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccb06a5c-fb0a-43dc-96a7-4c5304b66a86_1629x188.png 424w, https://substackcdn.com/image/fetch/$s_!moCf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccb06a5c-fb0a-43dc-96a7-4c5304b66a86_1629x188.png 848w, https://substackcdn.com/image/fetch/$s_!moCf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccb06a5c-fb0a-43dc-96a7-4c5304b66a86_1629x188.png 1272w, https://substackcdn.com/image/fetch/$s_!moCf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccb06a5c-fb0a-43dc-96a7-4c5304b66a86_1629x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!moCf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccb06a5c-fb0a-43dc-96a7-4c5304b66a86_1629x188.png" width="1456" height="168" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ccb06a5c-fb0a-43dc-96a7-4c5304b66a86_1629x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:168,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29717,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccb06a5c-fb0a-43dc-96a7-4c5304b66a86_1629x188.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_!moCf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccb06a5c-fb0a-43dc-96a7-4c5304b66a86_1629x188.png 424w, https://substackcdn.com/image/fetch/$s_!moCf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccb06a5c-fb0a-43dc-96a7-4c5304b66a86_1629x188.png 848w, https://substackcdn.com/image/fetch/$s_!moCf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccb06a5c-fb0a-43dc-96a7-4c5304b66a86_1629x188.png 1272w, https://substackcdn.com/image/fetch/$s_!moCf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccb06a5c-fb0a-43dc-96a7-4c5304b66a86_1629x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Scenario 5.4: Keep the original schedule after a rejected move</h3><h4>Business example</h4><p>A rejected move must not remove the original scheduled talk.</p><h4>Gherkin</h4><pre><code><code>Scenario: Keep the original schedule after a rejected move
  Given "Refactoring legacy code" is scheduled in Room A at 09:00
  When it is moved to an occupied room at 10:00
  Then "Refactoring legacy code" remains scheduled in Room A at 09:00</code></code></pre><h4>Diagram</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pnK0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc39b1d93-546b-440a-976f-77668d69f28c_2215x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pnK0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc39b1d93-546b-440a-976f-77668d69f28c_2215x188.png 424w, https://substackcdn.com/image/fetch/$s_!pnK0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc39b1d93-546b-440a-976f-77668d69f28c_2215x188.png 848w, https://substackcdn.com/image/fetch/$s_!pnK0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc39b1d93-546b-440a-976f-77668d69f28c_2215x188.png 1272w, https://substackcdn.com/image/fetch/$s_!pnK0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc39b1d93-546b-440a-976f-77668d69f28c_2215x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pnK0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc39b1d93-546b-440a-976f-77668d69f28c_2215x188.png" width="1456" height="124" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c39b1d93-546b-440a-976f-77668d69f28c_2215x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:124,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:38389,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc39b1d93-546b-440a-976f-77668d69f28c_2215x188.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_!pnK0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc39b1d93-546b-440a-976f-77668d69f28c_2215x188.png 424w, https://substackcdn.com/image/fetch/$s_!pnK0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc39b1d93-546b-440a-976f-77668d69f28c_2215x188.png 848w, https://substackcdn.com/image/fetch/$s_!pnK0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc39b1d93-546b-440a-976f-77668d69f28c_2215x188.png 1272w, https://substackcdn.com/image/fetch/$s_!pnK0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc39b1d93-546b-440a-976f-77668d69f28c_2215x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h1>Resume</h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T1VF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e31e033-7017-461e-a9d5-1615c5391329_1402x1122.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T1VF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e31e033-7017-461e-a9d5-1615c5391329_1402x1122.png 424w, https://substackcdn.com/image/fetch/$s_!T1VF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e31e033-7017-461e-a9d5-1615c5391329_1402x1122.png 848w, https://substackcdn.com/image/fetch/$s_!T1VF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e31e033-7017-461e-a9d5-1615c5391329_1402x1122.png 1272w, https://substackcdn.com/image/fetch/$s_!T1VF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e31e033-7017-461e-a9d5-1615c5391329_1402x1122.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T1VF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e31e033-7017-461e-a9d5-1615c5391329_1402x1122.png" width="1402" height="1122" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2e31e033-7017-461e-a9d5-1615c5391329_1402x1122.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1122,&quot;width&quot;:1402,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1548181,&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://emmanuelvalverderamos.substack.com/i/196390218?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e31e033-7017-461e-a9d5-1615c5391329_1402x1122.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_!T1VF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e31e033-7017-461e-a9d5-1615c5391329_1402x1122.png 424w, https://substackcdn.com/image/fetch/$s_!T1VF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e31e033-7017-461e-a9d5-1615c5391329_1402x1122.png 848w, https://substackcdn.com/image/fetch/$s_!T1VF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e31e033-7017-461e-a9d5-1615c5391329_1402x1122.png 1272w, https://substackcdn.com/image/fetch/$s_!T1VF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e31e033-7017-461e-a9d5-1615c5391329_1402x1122.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Stop making strategy without a map]]></title><description><![CDATA[Wardley Mapping helps teams understand where they are before deciding where to move]]></description><link>https://emmanuelvalverderamos.substack.com/p/stop-making-strategy-without-a-map</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/stop-making-strategy-without-a-map</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 14 Jul 2026 06:00:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_hT1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine asking for directions in a city you do not know and being told to move fast, stay aligned, and be innovative. None of that advice is useless. None of it tells you where to turn.</p><p>Strategy conversations often fail in the same way. The language sounds confident, but the landscape is missing. A team may have a roadmap, quarterly objectives, product bets, architecture diagrams, and budget commitments, yet still struggle to explain why one move makes sense now and another does not.</p><p>This is the problem Wardley Mapping tries to solve. It gives teams a way to make strategic context visible. Instead of debating from opinions, status, vendor pressure, or fashionable practices, the team can discuss users, needs, dependencies, maturity, constraints, and movement.</p><p>A Wardley map does not make the decision for you. It does something more practical: it improves the conversation before the decision is made.</p><h2>TL;DR</h2><ul><li><p>Wardley Mapping is a way to visualize strategic context by starting with users and their needs.</p></li><li><p>A map shows the value chain required to satisfy those needs, from visible user-facing capabilities to hidden enabling components.</p></li><li><p>Each component is placed on an evolution axis: genesis, custom built, product, and commodity or utility.</p></li><li><p>The same management approach should not be used for every component. Novel work needs learning, custom work needs close collaboration, product-like work needs evaluation, and commodity work needs standardization.</p></li><li><p>Wardley Mapping helps with product strategy, platform strategy, build versus buy, legacy modernization, cloud migration, outsourcing, team boundaries, and prioritization.</p></li><li><p>The goal is not to create a perfect drawing. The goal is to expose assumptions, find mismatches, and make better strategic moves.</p></li><li><p>A useful first map can be created around one real decision, one user, one need, and one value chain.</p></li><li><p>The article includes a complete worked example for an e-commerce self-service returns portal.</p></li></ul><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Index</h2><ol><li><p>Why strategy needs a map</p></li><li><p>What Wardley Mapping is</p></li><li><p>The two whys: purpose and movement</p></li><li><p>How a Wardley map works</p></li><li><p>The four stages of evolution</p></li><li><p>How to create your first map</p></li><li><p>Complete worked example: self-service returns portal</p></li><li><p>How to read map patterns</p></li><li><p>Real uses at work</p></li><li><p>Common mistakes</p></li><li><p>How to run a 90-minute mapping session</p></li><li><p>Conclusions</p></li><li><p>References</p></li></ol><h2>1. Why strategy needs a map</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-eI-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab5d3d40-bd93-4890-902a-d1f12d923ba1_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-eI-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab5d3d40-bd93-4890-902a-d1f12d923ba1_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!-eI-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab5d3d40-bd93-4890-902a-d1f12d923ba1_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!-eI-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab5d3d40-bd93-4890-902a-d1f12d923ba1_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!-eI-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab5d3d40-bd93-4890-902a-d1f12d923ba1_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-eI-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab5d3d40-bd93-4890-902a-d1f12d923ba1_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ab5d3d40-bd93-4890-902a-d1f12d923ba1_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:469237,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab5d3d40-bd93-4890-902a-d1f12d923ba1_1024x558.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_!-eI-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab5d3d40-bd93-4890-902a-d1f12d923ba1_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!-eI-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab5d3d40-bd93-4890-902a-d1f12d923ba1_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!-eI-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab5d3d40-bd93-4890-902a-d1f12d923ba1_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!-eI-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab5d3d40-bd93-4890-902a-d1f12d923ba1_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Most teams do not fail because they lack ambition. They fail because they lack position. They know what they want to achieve, but they cannot clearly explain where they are, what the landscape looks like, which constraints matter, and why this move is better than another.</p><p>This is why strategy documents can be dangerous. They often contain familiar words that make people feel safe: innovation, efficiency, alignment, customer-centricity, transformation, platform, ecosystem, Artificial Intelligence, cloud, modernization. These words may be useful, but they are not strategy by themselves. They do not show the user need, the dependencies, the maturity of each capability, the likely direction of change, or the cost of choosing one move over another.</p><p>A team can say, &#8220;We need a platform,&#8221; without showing which repeated needs the platform will serve. A company can say, &#8220;We need to modernize,&#8221; without identifying which part of the legacy system blocks valuable change. A product organization can say, &#8220;We need this feature,&#8221; without proving whether it addresses a real user need or merely satisfies a stakeholder request.</p><p>Wardley Mapping introduces discipline into this conversation. It asks the team to stop treating strategy as a polished story and start treating it as situated judgment. Before deciding what to do, the team must describe the landscape in which the decision lives.</p><p>That shift matters because software and product work are economic work. Every product decision, architecture decision, vendor decision, platform decision, and modernization decision spends money, time, attention, and organizational capacity. When the landscape is unclear, the investment is exposed to avoidable waste and risk.</p><h2>2. What Wardley Mapping is</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wf9H!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2afd2a5-05aa-454a-ad41-ce3ee48aa111_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wf9H!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2afd2a5-05aa-454a-ad41-ce3ee48aa111_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!wf9H!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2afd2a5-05aa-454a-ad41-ce3ee48aa111_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!wf9H!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2afd2a5-05aa-454a-ad41-ce3ee48aa111_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!wf9H!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2afd2a5-05aa-454a-ad41-ce3ee48aa111_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wf9H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2afd2a5-05aa-454a-ad41-ce3ee48aa111_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2afd2a5-05aa-454a-ad41-ce3ee48aa111_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:630848,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2afd2a5-05aa-454a-ad41-ce3ee48aa111_1024x558.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_!wf9H!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2afd2a5-05aa-454a-ad41-ce3ee48aa111_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!wf9H!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2afd2a5-05aa-454a-ad41-ce3ee48aa111_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!wf9H!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2afd2a5-05aa-454a-ad41-ce3ee48aa111_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!wf9H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2afd2a5-05aa-454a-ad41-ce3ee48aa111_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Wardley Mapping is a practice for visualizing a strategic landscape. It begins with users and their needs, then maps the chain of components required to satisfy those needs. Each component is positioned according to two dimensions: how visible it is to the user, and how evolved it is.</p><p>The vertical dimension is the value chain. Components near the top are more visible to the user. Components lower down are less visible, although they may still be essential. For example, a customer using an e-commerce returns portal cares about whether they can return an item easily, track the process, and receive a refund. They do not usually care whether the company hand-built its address validation service, hosting layer, or email delivery mechanism.</p><p>The horizontal dimension is evolution. Components move from uncertain and novel to more common, standardized, and expected. A new business capability may begin as exploration. Over time it may become custom built, then available as a product, and eventually treated as a commodity or utility.</p><p>This second dimension is what makes Wardley Mapping more than a dependency diagram. A dependency diagram may show that a returns portal needs payment integration, carrier integration, order history, and customer notifications. A Wardley map also asks whether those components are novel, custom, product-like, or commodity-like.</p><p>That question changes the strategy. If a component is commodity-like, handcrafting it may be wasteful. If a component is novel, treating it as predictable delivery may be naive. If a component is custom and strategically important, outsourcing it too early may move learning outside the organization. If a component is mature but critical, the right concern may be resilience, ownership, and operational quality rather than differentiation.</p><p>Wardley Mapping is not about making prettier diagrams. It is about making better decisions under uncertainty.</p><h2>3. The two whys: purpose and movement</h2><p>A useful distinction in Wardley Mapping is the difference between the why of purpose and the why of movement. The why of purpose explains what we are trying to achieve. The why of movement explains why this move makes sense from our current position.</p><p>Most organizations are more comfortable with purpose than movement. They can say they want better customer experience, faster delivery, lower cost, higher reliability, stronger product differentiation, or improved operational efficiency. Those goals may be valid, but they do not explain which move should happen next.</p><p>Consider a company that says, &#8220;We want to improve customer experience by making returns easier.&#8221; That is purpose. It matters, but it is not enough. The movement question is more specific: should the company build a custom returns portal, buy a returns management product, extend its commerce platform, improve customer support scripts, change return policy rules, or fix warehouse processing first?</p><p>Each of those moves may support the purpose. They are not equally good. Their value depends on the landscape: user needs, current capabilities, existing systems, operational constraints, component maturity, cost, risk, and timing.</p><p>This is where Wardley Mapping becomes useful. It helps the team connect purpose to movement. It does not stop at &#8220;we want better returns.&#8221; It asks what the customer needs, what capabilities satisfy that need, what those capabilities depend on, how evolved each dependency is, and which move improves the situation.</p><p>The practical consequence is simple. A strategy should not only explain what the organization wants. It should explain why this move is appropriate in this context.</p><h2>4. How a Wardley map works</h2><p>A Wardley map has a simple structure. It starts with a user. The user has needs. Those needs require capabilities. Those capabilities depend on other components. The map places those components vertically by visibility and horizontally by evolution.</p><p>The first anchor is the user. This prevents the conversation from starting with internal preferences. A team may want to sell a product, reduce cost, reuse a platform, adopt a vendor, or remove a legacy system. Those may be legitimate business concerns, but the map begins with the person or group receiving value.</p><p>The second element is the user need. This should be expressed as an outcome, not as a solution. &#8220;A returns portal&#8221; is not a user need. &#8220;The customer needs to know whether an item can be returned, start the process easily, and understand when the refund will arrive&#8221; is closer to a need.</p><p>The third element is the value chain. Once the need is clear, the team identifies what must exist to satisfy it. In an e-commerce returns flow, visible capabilities might include eligibility checking, return request creation, label generation, tracking, refund status, and customer notifications. Underneath those visible capabilities sit less visible components such as order history, product catalog, return policy rules, carrier integration, warehouse status, payment refund integration, fraud checks, authentication, address validation, monitoring, and hosting.</p><p>The fourth element is evolution. Each component is placed from left to right according to how mature and industrialized it is. The team asks whether the component is novel and uncertain, custom to the organization, available as a product, or commodity-like.</p><p>The map is useful because it combines these ideas in one shared view. It shows what the user values, what the organization must provide, what sits underneath the visible experience, and which components should be treated differently because they are at different stages of evolution.</p><p>A first map will not be perfect. That is acceptable. The value is not in producing a final artifact. The value is in making assumptions visible enough for the team to challenge them.</p><h2>5. The four stages of evolution</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uK5R!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09820bac-aee0-4e1e-88b7-3809f7b480ce_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uK5R!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09820bac-aee0-4e1e-88b7-3809f7b480ce_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!uK5R!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09820bac-aee0-4e1e-88b7-3809f7b480ce_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!uK5R!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09820bac-aee0-4e1e-88b7-3809f7b480ce_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!uK5R!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09820bac-aee0-4e1e-88b7-3809f7b480ce_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uK5R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09820bac-aee0-4e1e-88b7-3809f7b480ce_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/09820bac-aee0-4e1e-88b7-3809f7b480ce_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:585250,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09820bac-aee0-4e1e-88b7-3809f7b480ce_1024x558.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_!uK5R!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09820bac-aee0-4e1e-88b7-3809f7b480ce_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!uK5R!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09820bac-aee0-4e1e-88b7-3809f7b480ce_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!uK5R!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09820bac-aee0-4e1e-88b7-3809f7b480ce_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!uK5R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09820bac-aee0-4e1e-88b7-3809f7b480ce_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The evolution axis is central to Wardley Mapping because it changes how work should be treated. The same delivery model, funding model, governance model, and team structure should not be applied to every component.</p><h3>Genesis</h3><p>Genesis is the uncertain beginning. The component is novel, rare, unstable, and poorly understood. The team does not yet know exactly what shape it should take, whether users will value it, or which constraints will matter most.</p><p>Work in genesis needs learning. It should be approached through discovery, experiments, prototypes, fast feedback, and careful reduction of uncertainty. A fixed delivery promise can be dangerous here because the team may not yet understand what it is building.</p><p>In a returns example, a new predictive model for detecting return abuse in a highly specific market might be genesis if the organization has never tested it and does not know whether the signals are useful.</p><h3>Custom built</h3><p>Custom built means the component is understood enough to build, but it remains specific to the context. It may change often, depend on local knowledge, or reflect business-specific rules.</p><p>Work in this stage needs close collaboration. It often requires product, engineering, operations, legal, support, and domain experts to work together because the component encodes how the business actually works.</p><p>In a returns portal, return policy rules may be custom built. They may depend on country, product category, customer segment, promotional conditions, delivery date, item condition, fraud risk, and commercial strategy.</p><h3>Product</h3><p>A product-like component is more mature. Vendors exist. Patterns are known. Comparison is possible. The organization can evaluate options instead of inventing everything from scratch.</p><p>Work in this stage needs selection, integration, lifecycle management, and vendor understanding. The economic risk is excessive customization. A product can reduce cost and time, but only if the organization avoids bending it into a poor copy of its old process.</p><p>In a returns portal, carrier integration or customer notification tooling may be product-like. Many services already exist, and the team should be careful before building these capabilities from scratch.</p><h3>Commodity or utility</h3><p>Commodity or utility components are standardized, widespread, expected, and often invisible to the user. The user does not reward the company for handcrafting them, but failure may still be painful.</p><p>Work in this stage needs standardization, automation, operational discipline, cost awareness, and resilience. The goal is usually not differentiation. The goal is reliable, low-friction consumption.</p><p>In a returns portal, authentication, hosting, address validation, and basic logging are likely to be commodity-like or close to it. They may still require security and operational care, but they are rarely the strategic reason customers choose the company.</p><p>The important lesson is that evolution changes the economics of work. A component can begin as a source of differentiation and later become an expected utility. When that happens, the organization must change how it manages the component. Continuing to treat commodity work as bespoke craft consumes attention that could be used where learning or differentiation matter more.</p><h2>6. How to create your first map</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LdW3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fede011b4-7567-4b52-a30e-e3d26efe06eb_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LdW3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fede011b4-7567-4b52-a30e-e3d26efe06eb_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!LdW3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fede011b4-7567-4b52-a30e-e3d26efe06eb_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!LdW3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fede011b4-7567-4b52-a30e-e3d26efe06eb_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!LdW3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fede011b4-7567-4b52-a30e-e3d26efe06eb_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LdW3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fede011b4-7567-4b52-a30e-e3d26efe06eb_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ede011b4-7567-4b52-a30e-e3d26efe06eb_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:665640,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fede011b4-7567-4b52-a30e-e3d26efe06eb_1024x558.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_!LdW3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fede011b4-7567-4b52-a30e-e3d26efe06eb_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!LdW3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fede011b4-7567-4b52-a30e-e3d26efe06eb_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!LdW3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fede011b4-7567-4b52-a30e-e3d26efe06eb_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!LdW3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fede011b4-7567-4b52-a30e-e3d26efe06eb_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The easiest way to start is to map one real decision. Do not map the entire company. Do not start with an abstract transformation theme. Pick one product, one service, one user journey, one platform question, or one painful decision that people are already debating.</p><p>A good first question might be: should we build or buy this capability? Which part of this legacy system should we modernize first? Should we create a platform team for this area? Why is delivery slow in this workflow? Which parts of this service are differentiating and which are merely necessary?</p><p>Once the question is selected, identify the primary user. Be specific. &#8220;Customer&#8221; may be too broad. &#8220;Customer trying to return an item without contacting support&#8221; is better. &#8220;Developer deploying a service safely without waiting for another team&#8221; is better. &#8220;Finance analyst reconciling refunds at month end&#8221; is better.</p><p>After the user is clear, write the user need as an outcome. Avoid naming the solution too early. The user does not need &#8220;a dashboard&#8221; in the abstract. They may need to detect operational issues before customers complain. The user does not need &#8220;a portal&#8221; in the abstract. They may need to complete a return without uncertainty, delay, or avoidable support contact.</p><p>Next, build the value chain. Start with the visible capabilities that satisfy the need, then ask what each capability depends on. Continue downward until further decomposition stops being useful for the decision at hand. The goal is not to describe every technical detail. The goal is to reveal the components that influence the strategic choice.</p><p>Then place each component on the evolution axis. Ask whether it is novel, custom, product-like, or commodity-like. Be careful not to confuse how you currently treat the component with how evolved it is in the market. A company may handcraft something that is already available as a mature product. That does not make the component strategically custom. It may indicate waste, inertia, or historical accident.</p><p>Finally, discuss mismatches. Look for commodity components receiving custom investment, uncertain components being given fixed commitments, product-like components being over-customized, and strategically important knowledge being moved outside the organization too early.</p><p>At the end of this process, the team should be able to state what the map changes about the decision. If the map changes nothing, either the decision was already obvious or the map is too shallow.</p><h2>7. Complete worked example: self-service returns portal</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-T7E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14ccd0b1-f85c-402c-b92d-42fb6852b8f6_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-T7E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14ccd0b1-f85c-402c-b92d-42fb6852b8f6_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!-T7E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14ccd0b1-f85c-402c-b92d-42fb6852b8f6_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!-T7E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14ccd0b1-f85c-402c-b92d-42fb6852b8f6_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!-T7E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14ccd0b1-f85c-402c-b92d-42fb6852b8f6_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-T7E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14ccd0b1-f85c-402c-b92d-42fb6852b8f6_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/14ccd0b1-f85c-402c-b92d-42fb6852b8f6_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:627390,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14ccd0b1-f85c-402c-b92d-42fb6852b8f6_1024x558.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_!-T7E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14ccd0b1-f85c-402c-b92d-42fb6852b8f6_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!-T7E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14ccd0b1-f85c-402c-b92d-42fb6852b8f6_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!-T7E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14ccd0b1-f85c-402c-b92d-42fb6852b8f6_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!-T7E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14ccd0b1-f85c-402c-b92d-42fb6852b8f6_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Let us walk through a complete example. The situation is an e-commerce company that wants to reduce support volume and improve customer experience around product returns. Several people in the company are arguing about whether to build a custom self-service returns portal or buy a returns management product.</p><h3>Step 1: define the strategic question</h3><p>Question: should we build, buy, or delay a self-service returns portal for customers?</p><p>Answer: we should not decide at the level of &#8220;returns portal&#8221; because that phrase hides many components with different maturity, cost, risk, and user value. Some parts may be standard product capabilities. Some may be commodity infrastructure. Some may be business-specific and worth custom investment. We need to map the capability before choosing a build or buy answer.</p><p>This reframes the conversation. The team is no longer asking whether the whole thing should be built or bought. It is asking which parts of the capability deserve custom work, which should be consumed from the market, which should reuse existing internal capabilities, and which should be delayed.</p><h3>Step 2: identify the user</h3><p>Question: who is the primary user?</p><p>Answer: the primary user is the customer who wants to return a product without contacting support. Secondary users include support agents, warehouse teams, finance, operations, and fraud teams, but the first map should be anchored on the customer.</p><p>This matters because different users produce different maps. A customer-anchored map focuses on clarity, eligibility, instructions, tracking, and refund confidence. A warehouse-anchored map would focus more on intake processing, inspection, restocking, exception handling, and inventory accuracy. Both may be useful, but they are not the same map.</p><h3>Step 3: identify the user need</h3><p>Question: what does the customer need?</p><p>Answer: the customer needs to know whether the item can be returned, start the return easily, receive clear instructions, track the return status, and understand when the refund or exchange will happen. The customer does not primarily need &#8220;a portal&#8221;. The portal is one possible solution to the need.</p><p>This distinction prevents solution bias. If the team starts with &#8220;build a portal&#8221;, it may miss simpler improvements such as clearer order pages, better notifications, improved return policy wording, or tighter support automation. Starting with the need keeps the map honest.</p><h3>Step 4: identify visible capabilities</h3><p>Question: what capabilities directly satisfy the customer need?</p><p>Answer: the visible capabilities are return eligibility result, return request flow, return instructions, return label generation, return tracking, refund or exchange status, and customer notifications.</p><p>These are visible because the customer interacts with them directly or experiences their output. If they fail, the customer notices immediately. A confusing eligibility message, missing label, unclear instruction, or delayed refund status will generate support contact and reduce trust.</p><h3>Step 5: identify hidden dependencies</h3><p>Question: what does each visible capability depend on?</p><p>Answer: return eligibility depends on order history, product category, delivery date, return policy rules, item condition rules, customer history, and fraud checks. Label generation depends on carrier integration, address validation, country rules, shipping service selection, and packaging constraints. Refund or exchange status depends on payment provider integration, warehouse confirmation, finance rules, inventory status, and customer notifications.</p><p>This is where the idea starts becoming a map. The returns portal is not one thing. It is a visible customer experience sitting on top of policy, data, logistics, payments, warehouse operations, identity, communication, monitoring, and exception handling.</p><h3>Step 6: place components on the evolution axis</h3><p>The team now estimates where each component sits on the evolution axis. These placements are not universal truths. They are working assumptions for this company and this decision.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Q-6h!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff78b25f3-e106-4912-aac0-0a5c32d43c5c_2295x2107.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Q-6h!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff78b25f3-e106-4912-aac0-0a5c32d43c5c_2295x2107.png 424w, https://substackcdn.com/image/fetch/$s_!Q-6h!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff78b25f3-e106-4912-aac0-0a5c32d43c5c_2295x2107.png 848w, https://substackcdn.com/image/fetch/$s_!Q-6h!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff78b25f3-e106-4912-aac0-0a5c32d43c5c_2295x2107.png 1272w, https://substackcdn.com/image/fetch/$s_!Q-6h!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff78b25f3-e106-4912-aac0-0a5c32d43c5c_2295x2107.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Q-6h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff78b25f3-e106-4912-aac0-0a5c32d43c5c_2295x2107.png" width="2295" height="2107" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f78b25f3-e106-4912-aac0-0a5c32d43c5c_2295x2107.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2107,&quot;width&quot;:2295,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2820851,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28f4858c-3d4c-4997-9fad-d1861806a6d8_2340x2224.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_!Q-6h!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff78b25f3-e106-4912-aac0-0a5c32d43c5c_2295x2107.png 424w, https://substackcdn.com/image/fetch/$s_!Q-6h!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff78b25f3-e106-4912-aac0-0a5c32d43c5c_2295x2107.png 848w, https://substackcdn.com/image/fetch/$s_!Q-6h!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff78b25f3-e106-4912-aac0-0a5c32d43c5c_2295x2107.png 1272w, https://substackcdn.com/image/fetch/$s_!Q-6h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff78b25f3-e106-4912-aac0-0a5c32d43c5c_2295x2107.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The table is useful, but it is not yet the map. The map needs a visual arrangement where visibility and evolution can be seen together. A textual approximation can help in the article, but a final published version should include a proper image or SVG.</p><pre><code><code>Visibility

High
  Customer
|
| needs
v
  Return eligibility result   [Custom built]
  Return request flow [Product -&gt; Custom built]
  Return instructions [Custom built]
  Return label generation [Product]
  Return tracking [Product]
  Refund or exchange status   [Product -&gt; Custom built]

Medium
  Return policy rules [Custom built]
  Fraud and abuse checks  [Custom built]
  Customer notifications  [Product]
  Carrier integration [Product]
  Payment refund integration  [Product -&gt; Commodity]
  Inventory and warehouse update  [Custom built]

Low
  Order history   [Product -&gt; Custom built]
  Address validation  [Product -&gt; Commodity]
  Authentication  [Commodity]
  Monitoring and audit log[Product -&gt; Commodity]
  Hosting [Commodity -&gt; Utility]

Evolution
Genesis | Custom built | Product | Commodity / Utility
</code></code></pre><p>This approximation shows the reasoning, but a true Wardley map would place each component spatially on two axes. The vertical position would show visibility to the customer, and the horizontal position would show evolution. Lines would show dependencies. Annotations could mark inertia, risk, movement, or disagreement.</p><h3>Step 7: interpret the map</h3><p>Question: what does the map reveal?</p><p>Answer: the strategic investment is not generic hosting, authentication, address validation, basic carrier integration, or email infrastructure. Those components are mature enough that the company should usually reuse internal standards, buy products, or consume existing services. The more strategic areas are return policy rules, eligibility logic, fraud checks, warehouse integration, refund orchestration, and the customer-facing experience around uncertainty.</p><p>This immediately changes the build versus buy conversation. A full custom build would waste effort on mature components. A full product purchase might fail if the product cannot represent the company&#8217;s policy, warehouse process, fraud rules, and customer promise. The better answer is likely a mixed strategy.</p><p>Question: where is the main risk?</p><p>Answer: the main risk is not whether the company can display a form. The main risk is whether the eligibility decision is correct, whether refund timing matches the customer promise, whether warehouse operations can support the process, whether fraud controls are proportionate, and whether customers understand what is happening without contacting support.</p><p>This is a practical insight. If the team had started with user interface screens, it might have underestimated the operational and policy work underneath the visible portal. The map shows that a beautiful flow can still fail if the eligibility logic, warehouse events, refund rules, and notifications are unreliable.</p><p>Question: what should not be built from scratch?</p><p>Answer: the team should not build custom authentication, custom address validation, custom carrier integration, custom email infrastructure, or custom hosting unless there are strong constraints. Those components are not the differentiating parts of the landscape. Building them would increase cost and delay without improving the customer&#8217;s return experience in a meaningful way.</p><p>Question: what should the company learn before scaling?</p><p>Answer: the company should learn which return reasons are most common, which product categories create exceptions, where customers currently contact support, how often fraud checks are needed, how quickly the warehouse can process returns, and whether customers understand the return status messages. The first release should test these assumptions before the company commits to a broader platform.</p><h3>Step 8: derive a concrete strategy</h3><p>The map suggests that the team should not start by building a complete custom returns platform. It also suggests that buying a product and forcing every business rule into it may create problems if the company&#8217;s return policy and warehouse operations are specific.</p><p>A better strategy is to reuse or buy mature components while custom-building the policy and orchestration parts that reflect the company&#8217;s actual business. The team should use existing authentication, hosting, monitoring, address validation, notification infrastructure, and carrier services where possible. It should invest custom effort in eligibility rules, fraud checks, warehouse integration, refund orchestration, exception handling, and the customer-facing flow.</p><p>The first release should be deliberately narrow. For example, it could support one country, refund-only returns, a limited set of product categories, and customers with standard completed orders. It should exclude international returns, exchanges, damaged items, marketplace sellers, high-value fraud-risk items, and complex exceptions until the team has learned enough from the first slice.</p><p>The strategic decision is no longer &#8220;build or buy the returns portal&#8221;. The decision becomes more precise: buy or reuse mature components, build the business-specific logic, release a narrow slice, learn from real usage, and expand only after the risky assumptions have been tested.</p><p>That is the value of the map. It turns an abstract strategic argument into a concrete set of choices.</p><h2>8. How to read map patterns</h2><p>Once a map exists, the team needs to interpret it. The goal is not to admire the drawing. The goal is to find patterns that change the decision.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XmOm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2194451a-26c6-4298-8772-deeb0f0b8ed0_2340x1357.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XmOm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2194451a-26c6-4298-8772-deeb0f0b8ed0_2340x1357.png 424w, https://substackcdn.com/image/fetch/$s_!XmOm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2194451a-26c6-4298-8772-deeb0f0b8ed0_2340x1357.png 848w, https://substackcdn.com/image/fetch/$s_!XmOm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2194451a-26c6-4298-8772-deeb0f0b8ed0_2340x1357.png 1272w, https://substackcdn.com/image/fetch/$s_!XmOm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2194451a-26c6-4298-8772-deeb0f0b8ed0_2340x1357.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XmOm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2194451a-26c6-4298-8772-deeb0f0b8ed0_2340x1357.png" width="2340" height="1357" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2194451a-26c6-4298-8772-deeb0f0b8ed0_2340x1357.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1357,&quot;width&quot;:2340,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1915834,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e48a4f-dcbb-41ad-8174-034d5e9e10c6_2340x1424.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_!XmOm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2194451a-26c6-4298-8772-deeb0f0b8ed0_2340x1357.png 424w, https://substackcdn.com/image/fetch/$s_!XmOm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2194451a-26c6-4298-8772-deeb0f0b8ed0_2340x1357.png 848w, https://substackcdn.com/image/fetch/$s_!XmOm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2194451a-26c6-4298-8772-deeb0f0b8ed0_2340x1357.png 1272w, https://substackcdn.com/image/fetch/$s_!XmOm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2194451a-26c6-4298-8772-deeb0f0b8ed0_2340x1357.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>These patterns help move the article from beginner explanation to practical judgment. A reader should not only know what the axes mean. They should begin to understand what certain positions imply.</p><p>For example, custom work in commodity space does not automatically mean the team is wrong. There may be regulatory or operational constraints. But the map changes the burden of proof. If the market provides mature address validation, the team needs a strong reason to build and operate its own.</p><p>The same applies in the opposite direction. If a component is still uncertain, forcing it through a fixed delivery plan may create false confidence. In that case, the responsible move may be to reduce scope, run an experiment, and learn before scaling.</p><h2>9. Real uses at work</h2><p>Wardley Mapping is useful because many organizational decisions are really questions about user needs, dependencies, maturity, and movement. The map gives different functions a shared surface for discussing those questions.</p><h3>Product strategy</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_hT1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_hT1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.png 424w, https://substackcdn.com/image/fetch/$s_!_hT1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.png 848w, https://substackcdn.com/image/fetch/$s_!_hT1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!_hT1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_hT1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.png" width="1456" height="1165" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1165,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:151599,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.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_!_hT1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.png 424w, https://substackcdn.com/image/fetch/$s_!_hT1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.png 848w, https://substackcdn.com/image/fetch/$s_!_hT1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!_hT1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1addb7ac-09f7-4297-9ecc-bc34a1f5e7cd_1600x1280.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Product teams can use Wardley Mapping to separate user needs from feature requests. A stakeholder may ask for a dashboard, export, workflow, or portal, but the map asks what the user actually needs and what capabilities are required to satisfy that need.</p><p>In the returns example, &#8220;build a portal&#8221; was too broad. The map revealed several separate capabilities: eligibility, instructions, tracking, refund status, notifications, policy, fraud checks, and warehouse integration. That decomposition helps the product team prioritize the first useful slice.</p><h3>Platform strategy</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Pnzn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d0683a-7351-490a-9fca-c6d68d3a6b0e_1600x1280.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Pnzn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d0683a-7351-490a-9fca-c6d68d3a6b0e_1600x1280.png 424w, https://substackcdn.com/image/fetch/$s_!Pnzn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d0683a-7351-490a-9fca-c6d68d3a6b0e_1600x1280.png 848w, https://substackcdn.com/image/fetch/$s_!Pnzn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d0683a-7351-490a-9fca-c6d68d3a6b0e_1600x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!Pnzn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d0683a-7351-490a-9fca-c6d68d3a6b0e_1600x1280.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Pnzn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d0683a-7351-490a-9fca-c6d68d3a6b0e_1600x1280.png" width="1456" height="1165" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b6d0683a-7351-490a-9fca-c6d68d3a6b0e_1600x1280.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1165,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:142035,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d0683a-7351-490a-9fca-c6d68d3a6b0e_1600x1280.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_!Pnzn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d0683a-7351-490a-9fca-c6d68d3a6b0e_1600x1280.png 424w, https://substackcdn.com/image/fetch/$s_!Pnzn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d0683a-7351-490a-9fca-c6d68d3a6b0e_1600x1280.png 848w, https://substackcdn.com/image/fetch/$s_!Pnzn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d0683a-7351-490a-9fca-c6d68d3a6b0e_1600x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!Pnzn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d0683a-7351-490a-9fca-c6d68d3a6b0e_1600x1280.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Platform teams can use Wardley Mapping to avoid building platforms from internal enthusiasm alone. A platform should serve repeated user needs, usually for internal users such as developers, product teams, analysts, or operations teams.</p><p>The map helps identify which components are mature enough to standardize and which are still too unstable. Standardizing authentication, deployment templates, observability, or service ownership may improve flow. Standardizing a business workflow that is still changing may freeze learning too early.</p><h3>Build versus buy</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JQKE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0602a279-d147-48c8-bb6f-b4f98b484274_1600x1280.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JQKE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0602a279-d147-48c8-bb6f-b4f98b484274_1600x1280.png 424w, https://substackcdn.com/image/fetch/$s_!JQKE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0602a279-d147-48c8-bb6f-b4f98b484274_1600x1280.png 848w, https://substackcdn.com/image/fetch/$s_!JQKE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0602a279-d147-48c8-bb6f-b4f98b484274_1600x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!JQKE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0602a279-d147-48c8-bb6f-b4f98b484274_1600x1280.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JQKE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0602a279-d147-48c8-bb6f-b4f98b484274_1600x1280.png" width="1456" height="1165" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0602a279-d147-48c8-bb6f-b4f98b484274_1600x1280.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1165,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:135168,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0602a279-d147-48c8-bb6f-b4f98b484274_1600x1280.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_!JQKE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0602a279-d147-48c8-bb6f-b4f98b484274_1600x1280.png 424w, https://substackcdn.com/image/fetch/$s_!JQKE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0602a279-d147-48c8-bb6f-b4f98b484274_1600x1280.png 848w, https://substackcdn.com/image/fetch/$s_!JQKE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0602a279-d147-48c8-bb6f-b4f98b484274_1600x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!JQKE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0602a279-d147-48c8-bb6f-b4f98b484274_1600x1280.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Wardley Mapping improves build versus buy decisions because it shows that most capabilities are not one thing. A returns portal includes commodity components, product components, custom business rules, operational integrations, and user-facing experience.</p><p>The decision is rarely &#8220;build everything&#8221; or &#8220;buy everything&#8221;. A better decision may be to buy mature components, build the business-specific differentiators, and delay uncertain parts until the team has learned enough.</p><h3>Legacy modernization</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wF2m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa81e577b-9da3-45e9-89ef-d2aa3ed2dd7c_1600x1280.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wF2m!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa81e577b-9da3-45e9-89ef-d2aa3ed2dd7c_1600x1280.png 424w, https://substackcdn.com/image/fetch/$s_!wF2m!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa81e577b-9da3-45e9-89ef-d2aa3ed2dd7c_1600x1280.png 848w, https://substackcdn.com/image/fetch/$s_!wF2m!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa81e577b-9da3-45e9-89ef-d2aa3ed2dd7c_1600x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!wF2m!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa81e577b-9da3-45e9-89ef-d2aa3ed2dd7c_1600x1280.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wF2m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa81e577b-9da3-45e9-89ef-d2aa3ed2dd7c_1600x1280.png" width="1456" height="1165" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a81e577b-9da3-45e9-89ef-d2aa3ed2dd7c_1600x1280.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1165,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:140312,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa81e577b-9da3-45e9-89ef-d2aa3ed2dd7c_1600x1280.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_!wF2m!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa81e577b-9da3-45e9-89ef-d2aa3ed2dd7c_1600x1280.png 424w, https://substackcdn.com/image/fetch/$s_!wF2m!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa81e577b-9da3-45e9-89ef-d2aa3ed2dd7c_1600x1280.png 848w, https://substackcdn.com/image/fetch/$s_!wF2m!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa81e577b-9da3-45e9-89ef-d2aa3ed2dd7c_1600x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!wF2m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa81e577b-9da3-45e9-89ef-d2aa3ed2dd7c_1600x1280.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Modernization is often framed too broadly. A system is old, painful, or unpopular, so the organization decides to replace it. This can become expensive if the team does not know which parts of the system are actually blocking valuable change.</p><p>A Wardley map helps identify which components are still strategically important, which are commodity but trapped, which are risky, and which can be left alone for now. It also helps distinguish technical discomfort from economic urgency.</p><h3>Cloud migration</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FuQN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4a72725-3d71-475a-8b86-be45173c67eb_1600x1280.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FuQN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4a72725-3d71-475a-8b86-be45173c67eb_1600x1280.png 424w, https://substackcdn.com/image/fetch/$s_!FuQN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4a72725-3d71-475a-8b86-be45173c67eb_1600x1280.png 848w, https://substackcdn.com/image/fetch/$s_!FuQN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4a72725-3d71-475a-8b86-be45173c67eb_1600x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!FuQN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4a72725-3d71-475a-8b86-be45173c67eb_1600x1280.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FuQN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4a72725-3d71-475a-8b86-be45173c67eb_1600x1280.png" width="1456" height="1165" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a4a72725-3d71-475a-8b86-be45173c67eb_1600x1280.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1165,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:137410,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4a72725-3d71-475a-8b86-be45173c67eb_1600x1280.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_!FuQN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4a72725-3d71-475a-8b86-be45173c67eb_1600x1280.png 424w, https://substackcdn.com/image/fetch/$s_!FuQN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4a72725-3d71-475a-8b86-be45173c67eb_1600x1280.png 848w, https://substackcdn.com/image/fetch/$s_!FuQN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4a72725-3d71-475a-8b86-be45173c67eb_1600x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!FuQN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4a72725-3d71-475a-8b86-be45173c67eb_1600x1280.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Cloud migration should not be treated as a strategy by itself. It is usually a change in how underlying components are provided and operated. The map helps ask which components should move, why they should move, and what practices must change around them.</p><p>Moving infrastructure without changing ownership, deployment, observability, cost management, resilience, and operating model may produce a more expensive version of the old system. Mapping helps make those dependencies visible before the migration becomes a large commitment.</p><h3>Team boundaries</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!op69!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65363300-5ccf-4520-881c-967f87d7ee8e_1600x1280.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!op69!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65363300-5ccf-4520-881c-967f87d7ee8e_1600x1280.png 424w, https://substackcdn.com/image/fetch/$s_!op69!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65363300-5ccf-4520-881c-967f87d7ee8e_1600x1280.png 848w, https://substackcdn.com/image/fetch/$s_!op69!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65363300-5ccf-4520-881c-967f87d7ee8e_1600x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!op69!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65363300-5ccf-4520-881c-967f87d7ee8e_1600x1280.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!op69!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65363300-5ccf-4520-881c-967f87d7ee8e_1600x1280.png" width="1456" height="1165" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/65363300-5ccf-4520-881c-967f87d7ee8e_1600x1280.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1165,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:143873,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65363300-5ccf-4520-881c-967f87d7ee8e_1600x1280.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_!op69!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65363300-5ccf-4520-881c-967f87d7ee8e_1600x1280.png 424w, https://substackcdn.com/image/fetch/$s_!op69!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65363300-5ccf-4520-881c-967f87d7ee8e_1600x1280.png 848w, https://substackcdn.com/image/fetch/$s_!op69!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65363300-5ccf-4520-881c-967f87d7ee8e_1600x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!op69!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65363300-5ccf-4520-881c-967f87d7ee8e_1600x1280.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Team boundaries should support the flow of change. A map helps discuss whether teams should align around user needs, product areas, platforms, capabilities, or shared services.</p><p>A component in genesis may need a small exploratory team. A custom business capability may need close collaboration between product, engineering, operations, and domain experts. A commodity capability may belong in a platform, shared service, or external provider. The map helps avoid applying one team model to every kind of work.</p><h2>10. Common mistakes</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nP7r!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bf42e69-101c-47d9-80f2-0f383fcf125d_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nP7r!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bf42e69-101c-47d9-80f2-0f383fcf125d_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!nP7r!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bf42e69-101c-47d9-80f2-0f383fcf125d_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!nP7r!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bf42e69-101c-47d9-80f2-0f383fcf125d_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!nP7r!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bf42e69-101c-47d9-80f2-0f383fcf125d_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nP7r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bf42e69-101c-47d9-80f2-0f383fcf125d_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0bf42e69-101c-47d9-80f2-0f383fcf125d_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:715461,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bf42e69-101c-47d9-80f2-0f383fcf125d_1024x558.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_!nP7r!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bf42e69-101c-47d9-80f2-0f383fcf125d_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!nP7r!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bf42e69-101c-47d9-80f2-0f383fcf125d_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!nP7r!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bf42e69-101c-47d9-80f2-0f383fcf125d_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!nP7r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bf42e69-101c-47d9-80f2-0f383fcf125d_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The first common mistake is starting with technology instead of the user. If the conversation begins with Kubernetes, microservices, Artificial Intelligence, cloud, data mesh, or platform engineering, the team is already at risk of anchoring on a solution. Technology belongs on the map as a component, not as the starting point.</p><p>The second mistake is mapping the organization chart instead of the value chain. The fact that a team owns something does not explain whether it is visible to the user, strategically important, commodity-like, duplicated, risky, or evolving. Sometimes the map reveals that the current organization structure is part of the problem.</p><p>The third mistake is treating the map as objective truth. A Wardley map is a model. Its value comes from making assumptions visible, not from pretending the drawing is reality. The right phrase is not &#8220;the map says&#8221;. The better phrase is &#8220;our current understanding is&#8221;.</p><p>The fourth mistake is trying to make the perfect map. A useful map is good enough to improve the conversation. The first map should create challenge, reveal assumptions, and guide the next question. It does not need to describe every system, dependency, or exception.</p><p>The fifth mistake is confusing low visibility with low importance. Hosting, authentication, backups, observability, payment integrity, and audit logs may sit low on the map, but failure in these components can damage the entire user experience. Low visibility means the user does not usually value custom craftsmanship there. It does not mean the component can be neglected.</p><p>The sixth mistake is assuming commodity means outsource. A commodity component may still remain internal for regulatory, security, latency, resilience, cost, or control reasons. The point is not automatic outsourcing. The point is appropriate treatment: standardization, automation, operational discipline, and clear ownership.</p><p>The seventh mistake is assuming custom means strategic. Custom may mean valuable differentiation. It may also mean historical accident, unnecessary variation, local preference, or unresolved complexity. The map asks for evidence rather than accepting custom work as important by default.</p><p>The eighth mistake is drawing once and never updating the map. User needs change, components evolve, vendors mature, regulation appears, and inertia grows. A map should be revisited when the landscape changes or when the decision becomes important again.</p><h2>11. How to run a 90-minute mapping session</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wp9x!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3c0d559-50a1-485a-8fb9-accf6ccf451e_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wp9x!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3c0d559-50a1-485a-8fb9-accf6ccf451e_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!wp9x!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3c0d559-50a1-485a-8fb9-accf6ccf451e_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!wp9x!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3c0d559-50a1-485a-8fb9-accf6ccf451e_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!wp9x!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3c0d559-50a1-485a-8fb9-accf6ccf451e_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wp9x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3c0d559-50a1-485a-8fb9-accf6ccf451e_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e3c0d559-50a1-485a-8fb9-accf6ccf451e_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:790193,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3c0d559-50a1-485a-8fb9-accf6ccf451e_1024x558.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_!wp9x!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3c0d559-50a1-485a-8fb9-accf6ccf451e_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!wp9x!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3c0d559-50a1-485a-8fb9-accf6ccf451e_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!wp9x!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3c0d559-50a1-485a-8fb9-accf6ccf451e_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!wp9x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3c0d559-50a1-485a-8fb9-accf6ccf451e_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A first mapping session does not need to be complicated. The goal is to create a useful first map around a real decision, not to teach the entire theory of Wardley Mapping.</p><p>Invite a small cross-functional group. For a product or software decision, that might include product, engineering, operations, support, security, architecture, and someone close to the customer or user. The group should be small enough to have a real conversation and diverse enough to challenge assumptions.</p><p>Start with a decision statement. For example: &#8220;Should we build or buy a self-service returns capability?&#8221; Write the statement where everyone can see it. Then ask the group to identify the primary user and the user need. Spend enough time here, because a weak user need will distort the rest of the map.</p><p>Next, identify visible capabilities. Ask what the user experiences directly. In the returns example, this included eligibility, request creation, instructions, tracking, refund status, and notifications. Then move downward into dependencies. Ask what each capability needs in order to work.</p><p>Once the value chain is visible, place each component on the evolution axis. Do not let the group hide disagreement. If one person thinks return policy rules are custom and another thinks they are product-like, mark the disagreement. Disagreement is useful because it shows where assumptions differ.</p><p>After placement, look for mismatches. Ask where the organization is handcrafting mature components, where it is forcing certainty onto uncertain work, where it is over-customizing products, and where hidden dependencies create visible risk.</p><p>End the session with decisions and open questions. The output should not be only a map. It should include what the map changed, what the team now believes, what remains uncertain, what should be tested, and what move should happen next.</p><p>A simple 90-minute agenda could look like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DnpK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e741e06-f970-46dc-93b3-0666cf3b01b7_1382x616.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DnpK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e741e06-f970-46dc-93b3-0666cf3b01b7_1382x616.png 424w, https://substackcdn.com/image/fetch/$s_!DnpK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e741e06-f970-46dc-93b3-0666cf3b01b7_1382x616.png 848w, https://substackcdn.com/image/fetch/$s_!DnpK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e741e06-f970-46dc-93b3-0666cf3b01b7_1382x616.png 1272w, https://substackcdn.com/image/fetch/$s_!DnpK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e741e06-f970-46dc-93b3-0666cf3b01b7_1382x616.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DnpK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e741e06-f970-46dc-93b3-0666cf3b01b7_1382x616.png" width="1382" height="616" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0e741e06-f970-46dc-93b3-0666cf3b01b7_1382x616.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:616,&quot;width&quot;:1382,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:567034,&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://emmanuelvalverderamos.substack.com/i/196900527?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4051e919-eb89-4adf-bb26-f78861a22fab_1440x746.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_!DnpK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e741e06-f970-46dc-93b3-0666cf3b01b7_1382x616.png 424w, https://substackcdn.com/image/fetch/$s_!DnpK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e741e06-f970-46dc-93b3-0666cf3b01b7_1382x616.png 848w, https://substackcdn.com/image/fetch/$s_!DnpK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e741e06-f970-46dc-93b3-0666cf3b01b7_1382x616.png 1272w, https://substackcdn.com/image/fetch/$s_!DnpK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e741e06-f970-46dc-93b3-0666cf3b01b7_1382x616.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The facilitator&#8217;s job is not to defend the map. The facilitator&#8217;s job is to keep the conversation anchored on user needs, dependency, evolution, and decision quality.</p><h2>12. Conclusions</h2><p>Wardley Mapping is useful because it changes the quality of strategic conversation. It helps teams move from broad intention to situated judgment. Instead of asking only what the organization wants, the team asks who the user is, what the user needs, what components are required, how evolved those components are, and what move fits the landscape.</p><p>The most important lesson is not that every team needs a beautiful map. The important lesson is that strategy needs position. Without position, teams copy practices, overbuild commodity capabilities, standardize unstable work, outsource learning, and call internal preferences strategic.</p><p>In software and product work, this has a direct economic consequence. Poor strategic visibility can waste money, time, attention, and delivery capacity. It can also increase risk by hiding critical dependencies under user-facing plans. A Wardley map helps expose those commitments before they become expensive.</p><p>The practical next step is small. Pick one decision your team is already debating. Choose one user and one need. Draw the value chain. Place each component on the evolution axis. Discuss where the map changes the decision. If nothing changes, the map may be too shallow or the decision may already be obvious. If something changes, you have found the value of the practice.</p><h2>References</h2><ul><li><p>Wardley, Simon. <em>Wardley Maps</em>. <a href="https://learnwardleymapping.com/book/">https://learnwardleymapping.com/book/</a></p></li><li><p>Wardley, Simon. <em>On being lost</em>. <a href="https://medium.com/wardleymaps/on-being-lost-2ef5f05eb1ec">https://medium.com/wardleymaps/on-being-lost-2ef5f05eb1ec</a></p></li><li><p>Wardley, Simon. <em>Finding a path</em>. <a href="https://medium.com/wardleymaps/finding-a-path-cdb1249078c0">https://medium.com/wardleymaps/finding-a-path-cdb1249078c0</a></p></li><li><p>Wardley, Simon. <em>Exploring the map</em>. <a href="https://medium.com/wardleymaps/exploring-the-map-ad0266fad59b">https://medium.com/wardleymaps/exploring-the-map-ad0266fad59b</a></p></li><li><p>Wardley, Simon. <em>Doctrine</em>. <a href="https://medium.com/wardleymaps/doctrine-8bb0015688e5">https://medium.com/wardleymaps/doctrine-8bb0015688e5</a></p></li><li><p>Wardley, Simon. <em>The play and a decision to act</em>. <a href="https://medium.com/wardleymaps/the-play-and-a-decision-to-act-8eb796b1dff1">https://medium.com/wardleymaps/the-play-and-a-decision-to-act-8eb796b1dff1</a></p></li><li><p>Wardley, Simon. <em>Getting started yourself</em>. <a href="https://medium.com/wardleymaps/getting-started-yourself-e1a359b785a2">https://medium.com/wardleymaps/getting-started-yourself-e1a359b785a2</a></p></li><li><p>Wardley, Simon. <em>Finding a new purpose</em>. <a href="https://medium.com/wardleymaps/finding-a-new-purpose-8c60c9484d3b">https://medium.com/wardleymaps/finding-a-new-purpose-8c60c9484d3b</a></p></li><li><p>Wardley, Simon. <em>Keeping the wolves at bay</em>. <a href="https://medium.com/wardleymaps/keeping-the-wolves-at-bay-93de21b6b2f8">https://medium.com/wardleymaps/keeping-the-wolves-at-bay-93de21b6b2f8</a></p></li><li><p>Wardley, Simon. <em>Charting the future</em>. <a href="https://medium.com/wardleymaps/anticipation-89692e9b0ced">https://medium.com/wardleymaps/anticipation-89692e9b0ced</a></p></li><li><p>Wardley, Simon. <em>I wasn&#8217;t expecting that</em>. <a href="https://medium.com/wardleymaps/i-wasnt-expecting-that-dcfe122a2234">https://medium.com/wardleymaps/i-wasnt-expecting-that-dcfe122a2234</a></p></li><li><p>Wardley, Simon. <em>The scenario</em>. <a href="https://medium.com/wardleymaps/the-scenario-8bc05feee81">https://medium.com/wardleymaps/the-scenario-8bc05feee81</a></p></li><li><p>Wardley, Simon. <em>Something wicked this way comes</em>. <a href="https://medium.com/wardleymaps/something-wicked-this-way-comes-b028d5c607bf">https://medium.com/wardleymaps/something-wicked-this-way-comes-b028d5c607bf</a></p></li><li><p>Wardley, Simon. <em>On the practice of scenario planning</em>. <a href="https://medium.com/wardleymaps/on-the-practice-of-scenario-planning-49eed8279e90">https://medium.com/wardleymaps/on-the-practice-of-scenario-planning-49eed8279e90</a></p></li><li><p>Wardley, Simon. <em>Super Looper</em>. <a href="https://medium.com/wardleymaps/round-round-get-around-i-loop-around-d88e865d4337">https://medium.com/wardleymaps/round-round-get-around-i-loop-around-d88e865d4337</a></p></li><li><p>Wardley, Simon. <em>To infinity and beyond</em>. <a href="https://medium.com/wardleymaps/to-infinity-and-beyond-c7a53ccd2a07">https://medium.com/wardleymaps/to-infinity-and-beyond-c7a53ccd2a07</a></p></li><li><p>Wardley, Simon. <em>Better for Less</em>. <a href="https://medium.com/wardleymaps/better-for-less-58fe8c0a3aaa">https://medium.com/wardleymaps/better-for-less-58fe8c0a3aaa</a></p></li><li><p>Wardley, Simon. <em>On playing chess</em>. <a href="https://medium.com/wardleymaps/on-playing-chess-2634b825dbac">https://medium.com/wardleymaps/on-playing-chess-2634b825dbac</a></p></li><li><p>Sun Tzu. <em>The Art of War</em>.</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Responsibility-driven design: giving behaviour a responsible home]]></title><description><![CDATA[A practical way to understand Object-Oriented Programming (OOP) through responsibilities, roles, collaborations, contracts, and control style]]></description><link>https://emmanuelvalverderamos.substack.com/p/responsibility-driven-design-giving</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/responsibility-driven-design-giving</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 07 Jul 2026 06:00:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ud7N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17521a6a-4756-41ba-ac25-914217e4dbc5_812x1000.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There is a way of writing object-oriented code that looks correct from the outside and still feels wrong from the inside.</p><p>The code has classes. The classes have methods. The methods call other methods. There may even be interfaces, diagrams, and patterns. At first glance, everything appears to be in the right place.</p><p>But when you try to understand the behaviour, the design starts to resist.</p><p>One object asks too many questions. Another object holds data but makes no decisions. A service coordinates everything. A domain object is reduced to a container. A collaborator exists, but nobody can explain what role it plays. A method works, but it reads like a script that happens to be written inside a class.</p><p>This is one of the traps of Object-Oriented Programming (OOP): it is possible to use classes without really thinking in objects.</p><p>Responsibility-Driven Design (RDD) starts from a different place.</p><p>It does not begin by asking, &#8220;what classes do we need?&#8221;</p><p>It begins by asking, &#8220;what behaviour needs to happen, and who should be responsible for it?&#8221;</p><p>That question changes the design conversation.</p><p>Not because it gives us a mechanical recipe. It does not. But because it moves our attention from structure to responsibility, from data to behaviour, and from isolated classes to collaborating objects.</p><p>This article is about that shift.</p><h2>TL;DR</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yN3n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdca91fa9-d1a2-4ce4-aec3-a8391d8fc17c_1402x1122.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yN3n!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdca91fa9-d1a2-4ce4-aec3-a8391d8fc17c_1402x1122.png 424w, https://substackcdn.com/image/fetch/$s_!yN3n!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdca91fa9-d1a2-4ce4-aec3-a8391d8fc17c_1402x1122.png 848w, https://substackcdn.com/image/fetch/$s_!yN3n!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdca91fa9-d1a2-4ce4-aec3-a8391d8fc17c_1402x1122.png 1272w, https://substackcdn.com/image/fetch/$s_!yN3n!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdca91fa9-d1a2-4ce4-aec3-a8391d8fc17c_1402x1122.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yN3n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdca91fa9-d1a2-4ce4-aec3-a8391d8fc17c_1402x1122.png" width="1402" height="1122" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dca91fa9-d1a2-4ce4-aec3-a8391d8fc17c_1402x1122.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1122,&quot;width&quot;:1402,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1458600,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdca91fa9-d1a2-4ce4-aec3-a8391d8fc17c_1402x1122.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_!yN3n!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdca91fa9-d1a2-4ce4-aec3-a8391d8fc17c_1402x1122.png 424w, https://substackcdn.com/image/fetch/$s_!yN3n!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdca91fa9-d1a2-4ce4-aec3-a8391d8fc17c_1402x1122.png 848w, https://substackcdn.com/image/fetch/$s_!yN3n!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdca91fa9-d1a2-4ce4-aec3-a8391d8fc17c_1402x1122.png 1272w, https://substackcdn.com/image/fetch/$s_!yN3n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdca91fa9-d1a2-4ce4-aec3-a8391d8fc17c_1402x1122.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Responsibility-Driven Design (RDD) is a way of designing Object-Oriented Programming (OOP) systems by starting from <strong>responsibilities, roles, and collaborations</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_!W7Th!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d9cad1-1a05-49ce-b3c7-41d312291609_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!W7Th!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d9cad1-1a05-49ce-b3c7-41d312291609_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!W7Th!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d9cad1-1a05-49ce-b3c7-41d312291609_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!W7Th!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d9cad1-1a05-49ce-b3c7-41d312291609_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!W7Th!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d9cad1-1a05-49ce-b3c7-41d312291609_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!W7Th!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d9cad1-1a05-49ce-b3c7-41d312291609_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d4d9cad1-1a05-49ce-b3c7-41d312291609_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:613303,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d9cad1-1a05-49ce-b3c7-41d312291609_1024x559.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_!W7Th!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d9cad1-1a05-49ce-b3c7-41d312291609_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!W7Th!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d9cad1-1a05-49ce-b3c7-41d312291609_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!W7Th!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d9cad1-1a05-49ce-b3c7-41d312291609_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!W7Th!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4d9cad1-1a05-49ce-b3c7-41d312291609_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Its core idea is simple:</p><blockquote><p>Objects should not be treated as mere data containers. They should be responsible participants in a collaborating system.</p></blockquote><p>A responsibility is something an object is expected to know, do, decide, coordinate, translate, protect, or delegate. A role is the position an object plays in a scenario. A collaboration happens when one object asks another object for help by sending a message. A contract describes what that message means: what the receiver expects, what it promises, and what the caller should not need to know.</p><p>The point of Responsibility-Driven Design (RDD) is not to create more classes. It is to distribute behaviour well.</p><p>A good object-oriented design is not a pile of named things. It is a community of objects that know what they are responsible for and know when to ask one another for help.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ud7N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17521a6a-4756-41ba-ac25-914217e4dbc5_812x1000.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ud7N!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17521a6a-4756-41ba-ac25-914217e4dbc5_812x1000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!ud7N!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17521a6a-4756-41ba-ac25-914217e4dbc5_812x1000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!ud7N!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17521a6a-4756-41ba-ac25-914217e4dbc5_812x1000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!ud7N!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17521a6a-4756-41ba-ac25-914217e4dbc5_812x1000.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ud7N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17521a6a-4756-41ba-ac25-914217e4dbc5_812x1000.jpeg" width="812" height="1000" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17521a6a-4756-41ba-ac25-914217e4dbc5_812x1000.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1000,&quot;width&quot;:812,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Object Design: Roles, Responsibilities, and Collaborations: Wirfs-Brock,  Rebecca, McKean, Alan: 9780201379433: Amazon.com: Books&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Object Design: Roles, Responsibilities, and Collaborations: Wirfs-Brock,  Rebecca, McKean, Alan: 9780201379433: Amazon.com: Books" title="Object Design: Roles, Responsibilities, and Collaborations: Wirfs-Brock,  Rebecca, McKean, Alan: 9780201379433: Amazon.com: Books" srcset="https://substackcdn.com/image/fetch/$s_!ud7N!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17521a6a-4756-41ba-ac25-914217e4dbc5_812x1000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!ud7N!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17521a6a-4756-41ba-ac25-914217e4dbc5_812x1000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!ud7N!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17521a6a-4756-41ba-ac25-914217e4dbc5_812x1000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!ud7N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17521a6a-4756-41ba-ac25-914217e4dbc5_812x1000.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Index</h2><ol><li><p>Why classes are not enough</p></li><li><p>Object thinking before object design</p></li><li><p>What Responsibility-Driven Design (RDD) is really about</p></li><li><p>Responsibilities before structure</p></li><li><p>Doing and knowing responsibilities</p></li><li><p>Roles and role stereotypes</p></li><li><p>Finding candidate objects</p></li><li><p>Assigning responsibilities</p></li><li><p>Collaborations, messages, and contracts</p></li><li><p>Class, Responsibility, Collaborator (CRC) cards</p></li><li><p>Control style: centralized, dispersed, delegated</p></li><li><p>A practical example in Kotlin</p></li><li><p>Trust boundaries and reliable collaborations</p></li><li><p>Flexibility and variation points</p></li><li><p>Patterns as responsibility arrangements</p></li><li><p>Conclusion</p></li><li><p>References</p></li></ol><h2>1. Why classes are not enough</h2><p>A common mistake in Object-Oriented Programming (OOP) is to think that object-oriented design is mainly about finding classes.</p><p>It is understandable. Requirements often contain nouns, and nouns often look like objects. A library has members, books, loans, reservations, librarians, fines, and notifications. It is tempting to turn those words into classes and feel that the design has begun.</p><p>Sometimes that is useful. Some of those concepts may deserve representation in code.</p><p>But a list of nouns is not a design.</p><p>The problem is not noticing candidate objects. The problem is treating those candidates as if they already explain the behaviour of the system.</p><p>A class called <code>Loan</code> does not tell us who is responsible for creating a loan. A class called <code>Member</code> does not tell us who decides whether that member may borrow another book. A class called <code>Notification</code> does not tell us who should be told, when, or why. The class name gives us a possible concept, but not yet a distribution of responsibilities.</p><p>That is where many object-oriented designs become weak. They have objects, but the objects do not carry enough behaviour. They hold data, expose state, and wait for a central service to do the real work.</p><p>The design is object-shaped, but the thinking is still procedural.</p><p>Responsibility-Driven Design (RDD) pushes against that. It asks us to begin with behaviour.</p><p>Not &#8220;what entities exist?&#8221;<br>But &#8220;what work must be done?&#8221;</p><p>Not &#8220;what data does this object have?&#8221;<br>But &#8220;what is this object responsible for?&#8221;</p><p>Not &#8220;which class should hold this field?&#8221;<br>But &#8220;who should know this, decide this, or do this?&#8221;</p><p>That is the first important shift:</p><blockquote><p>A class becomes useful when it gives a responsibility a good home.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GerK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c155a16-d5dc-42e0-8859-3d872df44dae_2956x420.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GerK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c155a16-d5dc-42e0-8859-3d872df44dae_2956x420.png 424w, https://substackcdn.com/image/fetch/$s_!GerK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c155a16-d5dc-42e0-8859-3d872df44dae_2956x420.png 848w, https://substackcdn.com/image/fetch/$s_!GerK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c155a16-d5dc-42e0-8859-3d872df44dae_2956x420.png 1272w, https://substackcdn.com/image/fetch/$s_!GerK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c155a16-d5dc-42e0-8859-3d872df44dae_2956x420.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GerK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c155a16-d5dc-42e0-8859-3d872df44dae_2956x420.png" width="1456" height="207" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8c155a16-d5dc-42e0-8859-3d872df44dae_2956x420.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:207,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:71421,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c155a16-d5dc-42e0-8859-3d872df44dae_2956x420.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_!GerK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c155a16-d5dc-42e0-8859-3d872df44dae_2956x420.png 424w, https://substackcdn.com/image/fetch/$s_!GerK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c155a16-d5dc-42e0-8859-3d872df44dae_2956x420.png 848w, https://substackcdn.com/image/fetch/$s_!GerK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c155a16-d5dc-42e0-8859-3d872df44dae_2956x420.png 1272w, https://substackcdn.com/image/fetch/$s_!GerK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c155a16-d5dc-42e0-8859-3d872df44dae_2956x420.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><pre><code><code>flowchart LR
    Requirement[Requirement] --&gt; CandidateNouns[Candidate nouns]
    CandidateNouns --&gt; Classes[Classes]
    Classes --&gt; Data[Fields and data]
    Data --&gt; CentralService[Central service makes decisions]

    Requirement --&gt; Behaviour[Behaviour that must happen]
    Behaviour --&gt; Responsibilities[Responsibilities]
    Responsibilities --&gt; Roles[Roles]
    Roles --&gt; Collaborations[Collaborations]
    Collaborations --&gt; ResponsibleObjects[Responsible objects]</code></code></pre><p>The second path does not reject classes. It simply refuses to let classes lead the design before responsibilities are understood.</p><h2>2. Object thinking before object design</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OPBX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e68a211-e3f2-497a-8838-570f4d06cea3_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OPBX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e68a211-e3f2-497a-8838-570f4d06cea3_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!OPBX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e68a211-e3f2-497a-8838-570f4d06cea3_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!OPBX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e68a211-e3f2-497a-8838-570f4d06cea3_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!OPBX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e68a211-e3f2-497a-8838-570f4d06cea3_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OPBX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e68a211-e3f2-497a-8838-570f4d06cea3_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6e68a211-e3f2-497a-8838-570f4d06cea3_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:656449,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e68a211-e3f2-497a-8838-570f4d06cea3_1024x558.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_!OPBX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e68a211-e3f2-497a-8838-570f4d06cea3_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!OPBX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e68a211-e3f2-497a-8838-570f4d06cea3_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!OPBX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e68a211-e3f2-497a-8838-570f4d06cea3_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!OPBX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e68a211-e3f2-497a-8838-570f4d06cea3_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Before Responsibility-Driven Design (RDD) becomes a technique, it rests on a way of seeing software.</p><p>Object thinking asks us to stop seeing software as procedures operating over data. Instead, we see a system as a community of objects that know things, do things, make decisions, and collaborate.</p><p>The important shift is not syntax. A programming language can support classes and still allow procedural design. The deeper shift is cognitive.</p><p>A procedural instinct asks:</p><blockquote><p>What sequence of steps should I write?</p></blockquote><p>An object thinking instinct asks:</p><blockquote><p>Which responsible participants should collaborate to make this behaviour happen?</p></blockquote><p>That difference matters because it changes what we notice.</p><p>If we think procedurally, we tend to look for the flow first. We ask where the process starts, which data is needed, which functions transform it, and where the result goes. This can be useful for understanding an algorithm, but it often creates designs where objects become passive containers passed through a central process.</p><p>If we think in objects, we look for participants. We ask what each participant is responsible for, what it can do for others, what it should hide, and when it needs help.</p><p>The system is no longer just a sequence of steps.</p><p>It becomes a network of responsibilities.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!n_Ql!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cc028de-37a7-4328-a416-111689586a3f_1852x543.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!n_Ql!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cc028de-37a7-4328-a416-111689586a3f_1852x543.png 424w, https://substackcdn.com/image/fetch/$s_!n_Ql!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cc028de-37a7-4328-a416-111689586a3f_1852x543.png 848w, https://substackcdn.com/image/fetch/$s_!n_Ql!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cc028de-37a7-4328-a416-111689586a3f_1852x543.png 1272w, https://substackcdn.com/image/fetch/$s_!n_Ql!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cc028de-37a7-4328-a416-111689586a3f_1852x543.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!n_Ql!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cc028de-37a7-4328-a416-111689586a3f_1852x543.png" width="1852" height="543" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2cc028de-37a7-4328-a416-111689586a3f_1852x543.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:543,&quot;width&quot;:1852,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:95139,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c170a05-e134-41ba-841b-56b8231891aa_2800x696.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_!n_Ql!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cc028de-37a7-4328-a416-111689586a3f_1852x543.png 424w, https://substackcdn.com/image/fetch/$s_!n_Ql!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cc028de-37a7-4328-a416-111689586a3f_1852x543.png 848w, https://substackcdn.com/image/fetch/$s_!n_Ql!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cc028de-37a7-4328-a416-111689586a3f_1852x543.png 1272w, https://substackcdn.com/image/fetch/$s_!n_Ql!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cc028de-37a7-4328-a416-111689586a3f_1852x543.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This does not mean every algorithm disappears. It means the design is no longer organized around one controlling procedure that interrogates everything else.</p><p>A good object-oriented design gives behaviour to objects that can own it.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VpoZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed51bcc2-e4a7-438e-b055-96833cca3322_2606x556.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VpoZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed51bcc2-e4a7-438e-b055-96833cca3322_2606x556.png 424w, https://substackcdn.com/image/fetch/$s_!VpoZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed51bcc2-e4a7-438e-b055-96833cca3322_2606x556.png 848w, https://substackcdn.com/image/fetch/$s_!VpoZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed51bcc2-e4a7-438e-b055-96833cca3322_2606x556.png 1272w, https://substackcdn.com/image/fetch/$s_!VpoZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed51bcc2-e4a7-438e-b055-96833cca3322_2606x556.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VpoZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed51bcc2-e4a7-438e-b055-96833cca3322_2606x556.png" width="1456" height="311" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ed51bcc2-e4a7-438e-b055-96833cca3322_2606x556.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:311,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:84546,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed51bcc2-e4a7-438e-b055-96833cca3322_2606x556.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_!VpoZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed51bcc2-e4a7-438e-b055-96833cca3322_2606x556.png 424w, https://substackcdn.com/image/fetch/$s_!VpoZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed51bcc2-e4a7-438e-b055-96833cca3322_2606x556.png 848w, https://substackcdn.com/image/fetch/$s_!VpoZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed51bcc2-e4a7-438e-b055-96833cca3322_2606x556.png 1272w, https://substackcdn.com/image/fetch/$s_!VpoZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed51bcc2-e4a7-438e-b055-96833cca3322_2606x556.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><pre><code><code>flowchart TD
    ObjectThinking[Object thinking] --&gt; Behaviour[Behaviour first]
    ObjectThinking --&gt; Responsibilities[Responsibilities]
    ObjectThinking --&gt; Collaboration[Collaboration]
    ObjectThinking --&gt; Encapsulation[Encapsulation]
    ObjectThinking --&gt; DistributedControl[Distributed control]

    Behaviour --&gt; WhatHappens[What must happen?]
    Responsibilities --&gt; WhoOwnsIt[Who should own it?]
    Collaboration --&gt; WhoHelps[Who needs to help?]
    Encapsulation --&gt; WhatIsHidden[What should stay hidden?]
    DistributedControl --&gt; WhereControlLives[Where should control live?]</code></code></pre><p>Responsibility-Driven Design (RDD) gives this way of thinking a practical vocabulary.</p><h2>3. What Responsibility-Driven Design (RDD) is really about</h2><p>Responsibility-Driven Design (RDD) is a way of designing object-oriented systems by focusing on responsibilities, roles, and collaborations.</p><p>A responsibility is an obligation.</p><p>An object may be responsible for knowing something. It may be responsible for doing something. It may be responsible for deciding something. It may be responsible for coordinating a process. It may be responsible for translating between the system and an external boundary. It may be responsible for protecting an invariant. It may be responsible for delegating part of its work to another object.</p><p>The important part is that responsibility is behavioural language.</p><p>It tells us what an object contributes to the system.</p><p>That is why Responsibility-Driven Design (RDD) is not just a technique for finding classes. It is a way to reason about how behaviour should be distributed.</p><p>The practical questions are:</p><ul><li><p>What responsibilities exist?</p></li><li><p>Which roles are needed?</p></li><li><p>Which object should carry each responsibility?</p></li><li><p>Which objects must collaborate?</p></li><li><p>What contract does each collaboration imply?</p></li><li><p>Where should control live?</p></li><li><p>Which details should be hidden?</p></li><li><p>Which boundaries are trustworthy?</p></li><li><p>Where is flexibility actually valuable?</p></li></ul><p>These questions are not theoretical decoration. They are the questions we need when code starts to feel wrong.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rsAc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51a17530-4651-46a7-913a-4058d4f461a6_2722x556.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rsAc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51a17530-4651-46a7-913a-4058d4f461a6_2722x556.png 424w, https://substackcdn.com/image/fetch/$s_!rsAc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51a17530-4651-46a7-913a-4058d4f461a6_2722x556.png 848w, https://substackcdn.com/image/fetch/$s_!rsAc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51a17530-4651-46a7-913a-4058d4f461a6_2722x556.png 1272w, https://substackcdn.com/image/fetch/$s_!rsAc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51a17530-4651-46a7-913a-4058d4f461a6_2722x556.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rsAc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51a17530-4651-46a7-913a-4058d4f461a6_2722x556.png" width="1456" height="297" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/51a17530-4651-46a7-913a-4058d4f461a6_2722x556.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:297,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59358,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51a17530-4651-46a7-913a-4058d4f461a6_2722x556.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_!rsAc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51a17530-4651-46a7-913a-4058d4f461a6_2722x556.png 424w, https://substackcdn.com/image/fetch/$s_!rsAc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51a17530-4651-46a7-913a-4058d4f461a6_2722x556.png 848w, https://substackcdn.com/image/fetch/$s_!rsAc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51a17530-4651-46a7-913a-4058d4f461a6_2722x556.png 1272w, https://substackcdn.com/image/fetch/$s_!rsAc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51a17530-4651-46a7-913a-4058d4f461a6_2722x556.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><pre><code><code>flowchart LR
    Scenario[Scenario] --&gt; Responsibilities[Responsibilities]
    Responsibilities --&gt; Roles[Roles]
    Roles --&gt; Candidates[Candidate objects]
    Candidates --&gt; Collaborations[Collaborations]
    Collaborations --&gt; Contracts[Contracts]
    Contracts --&gt; Protocols[Protocols]
    Collaborations --&gt; ControlStyle[Control style]
    Collaborations --&gt; Trust[Trust boundaries]</code></code></pre><p>The diagram is not a process that must be followed mechanically. It is a map of design concerns. In real work, we move back and forth between them.</p><h2>4. Responsibilities before structure</h2><p>The phrase &#8220;responsibilities before structure&#8221; is easy to say, but it changes the design conversation.</p><p>Imagine we are designing a library lending system.</p><p>A structure-first approach might begin with:</p><ul><li><p><code>Member</code></p></li><li><p><code>Book</code></p></li><li><p><code>BookCopy</code></p></li><li><p><code>Loan</code></p></li><li><p><code>Reservation</code></p></li><li><p><code>Fine</code></p></li><li><p><code>Notification</code></p></li></ul><p>That list may contain useful candidates. But it does not yet tell us how the system behaves.</p><p>A responsibility-first approach asks different questions:</p><ul><li><p>Who decides whether a member may borrow a book copy?</p></li><li><p>Who records that a book copy is now on loan?</p></li><li><p>Who knows when the loan is due?</p></li><li><p>Who decides whether the loan is overdue?</p></li><li><p>Who notifies the member?</p></li><li><p>Who coordinates the lending process?</p></li><li><p>Who talks to persistence?</p></li><li><p>Who talks to the notification channel?</p></li></ul><p>Now we are no longer just naming concepts. We are distributing work.</p><p>That distribution is the heart of design.</p><p>A responsibility map might begin like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IVXz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91ec6083-5bab-4e71-80ab-25fc4836b3cb_2750x703.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IVXz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91ec6083-5bab-4e71-80ab-25fc4836b3cb_2750x703.png 424w, https://substackcdn.com/image/fetch/$s_!IVXz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91ec6083-5bab-4e71-80ab-25fc4836b3cb_2750x703.png 848w, https://substackcdn.com/image/fetch/$s_!IVXz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91ec6083-5bab-4e71-80ab-25fc4836b3cb_2750x703.png 1272w, https://substackcdn.com/image/fetch/$s_!IVXz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91ec6083-5bab-4e71-80ab-25fc4836b3cb_2750x703.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IVXz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91ec6083-5bab-4e71-80ab-25fc4836b3cb_2750x703.png" width="2750" height="703" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/91ec6083-5bab-4e71-80ab-25fc4836b3cb_2750x703.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:703,&quot;width&quot;:2750,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:169113,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b6dcb9f-0687-48fe-a032-f096f57ed79b_2800x860.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_!IVXz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91ec6083-5bab-4e71-80ab-25fc4836b3cb_2750x703.png 424w, https://substackcdn.com/image/fetch/$s_!IVXz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91ec6083-5bab-4e71-80ab-25fc4836b3cb_2750x703.png 848w, https://substackcdn.com/image/fetch/$s_!IVXz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91ec6083-5bab-4e71-80ab-25fc4836b3cb_2750x703.png 1272w, https://substackcdn.com/image/fetch/$s_!IVXz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91ec6083-5bab-4e71-80ab-25fc4836b3cb_2750x703.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This table is not the final design. It is a way to make the work visible.</p><p>That is one of the first things Responsibility-Driven Design (RDD) gives us: it makes responsibility visible before structure becomes fixed.</p><h2>5. Doing and knowing responsibilities</h2><p>Responsibilities are not all the same kind.</p><p>A useful distinction in Responsibility-Driven Design (RDD) is the difference between <strong>doing responsibilities</strong> and <strong>knowing responsibilities</strong>.</p><p>A doing responsibility is about behaviour. It is the responsibility to calculate, validate, reserve, lend, notify, coordinate, translate, decide, or delegate.</p><p>A knowing responsibility is about information. It is the responsibility to know the member identifier, know the book copy identifier, know the due date, know whether a loan is active, or know which rules apply to a member.</p><p>This distinction is useful, but it should not be made too simplistic. Objects often do both.</p><p>For example, an <code>ActiveLoan</code> may know its due date, but it may also answer whether it is overdue on a given date. A <code>LoanPeriod</code> may know a start date and due date, but it may also calculate whether a date is within the period.</p><p>The point is not to force objects into boxes.</p><p>The point is to notice what kind of responsibility we are assigning.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kxRf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ec01fe-c4f7-46b1-bc1f-2c460661d061_2754x564.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kxRf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ec01fe-c4f7-46b1-bc1f-2c460661d061_2754x564.png 424w, https://substackcdn.com/image/fetch/$s_!kxRf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ec01fe-c4f7-46b1-bc1f-2c460661d061_2754x564.png 848w, https://substackcdn.com/image/fetch/$s_!kxRf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ec01fe-c4f7-46b1-bc1f-2c460661d061_2754x564.png 1272w, https://substackcdn.com/image/fetch/$s_!kxRf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ec01fe-c4f7-46b1-bc1f-2c460661d061_2754x564.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kxRf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ec01fe-c4f7-46b1-bc1f-2c460661d061_2754x564.png" width="2754" height="564" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e5ec01fe-c4f7-46b1-bc1f-2c460661d061_2754x564.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:564,&quot;width&quot;:2754,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:143486,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff903a90a-7668-4ec4-b7f2-e0713f6ca800_2800x696.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_!kxRf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ec01fe-c4f7-46b1-bc1f-2c460661d061_2754x564.png 424w, https://substackcdn.com/image/fetch/$s_!kxRf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ec01fe-c4f7-46b1-bc1f-2c460661d061_2754x564.png 848w, https://substackcdn.com/image/fetch/$s_!kxRf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ec01fe-c4f7-46b1-bc1f-2c460661d061_2754x564.png 1272w, https://substackcdn.com/image/fetch/$s_!kxRf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ec01fe-c4f7-46b1-bc1f-2c460661d061_2754x564.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>This matters because different responsibilities lead to different design pressures. A coordinator should not absorb every rule. A value should not be forced to know external systems. A boundary object should not leak external vocabulary into the domain.</p><p>Responsibility type helps us ask better questions.</p><h2>6. Roles and role stereotypes</h2><p>A common weak definition of an object is this: an object is data plus methods.</p><p>That definition is not completely wrong, but it is not strong enough for design.</p><p>It keeps our attention on the wrong thing. It makes us think of objects as containers that happen to have functions attached. That is how we end up with data holders, getters, setters, and one large service making decisions from the outside.</p><p>A better design definition is this:</p><blockquote><p>An object is a responsible participant that plays a role in a collaboration.</p></blockquote><p>A role is not the same thing as a class. A class is a code structure. A role is a behavioural position in a scenario.</p><p>Role stereotypes help us describe that position. They are not rigid categories. They are thinking tools.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EYfw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0abf29-d049-4ea7-ba12-6832a665e2eb_2743x632.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EYfw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0abf29-d049-4ea7-ba12-6832a665e2eb_2743x632.png 424w, https://substackcdn.com/image/fetch/$s_!EYfw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0abf29-d049-4ea7-ba12-6832a665e2eb_2743x632.png 848w, https://substackcdn.com/image/fetch/$s_!EYfw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0abf29-d049-4ea7-ba12-6832a665e2eb_2743x632.png 1272w, https://substackcdn.com/image/fetch/$s_!EYfw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0abf29-d049-4ea7-ba12-6832a665e2eb_2743x632.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EYfw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0abf29-d049-4ea7-ba12-6832a665e2eb_2743x632.png" width="2743" height="632" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf0abf29-d049-4ea7-ba12-6832a665e2eb_2743x632.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:632,&quot;width&quot;:2743,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:201212,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e23546e-83c4-476c-b72e-568f3f9d9aac_2800x778.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_!EYfw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0abf29-d049-4ea7-ba12-6832a665e2eb_2743x632.png 424w, https://substackcdn.com/image/fetch/$s_!EYfw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0abf29-d049-4ea7-ba12-6832a665e2eb_2743x632.png 848w, https://substackcdn.com/image/fetch/$s_!EYfw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0abf29-d049-4ea7-ba12-6832a665e2eb_2743x632.png 1272w, https://substackcdn.com/image/fetch/$s_!EYfw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf0abf29-d049-4ea7-ba12-6832a665e2eb_2743x632.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>A controller is not automatically a design mistake. It becomes dangerous when close direction turns into centralized intelligence and the rest of the model becomes passive.</p><p>An information holder is not automatically weak either. Some objects legitimately hold information. The problem appears when information holding becomes the whole model and behaviour migrates elsewhere.</p><p>The value of the stereotypes is that they help us see emphasis.</p><p>They let us ask:</p><blockquote><p>Is this object&#8217;s role clear?</p></blockquote><p>That is more useful than asking whether the object has enough methods.</p><h2>7. Finding candidate objects</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!utTe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07b488d5-7b67-4616-ac8c-13a52487575b_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!utTe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07b488d5-7b67-4616-ac8c-13a52487575b_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!utTe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07b488d5-7b67-4616-ac8c-13a52487575b_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!utTe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07b488d5-7b67-4616-ac8c-13a52487575b_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!utTe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07b488d5-7b67-4616-ac8c-13a52487575b_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!utTe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07b488d5-7b67-4616-ac8c-13a52487575b_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/07b488d5-7b67-4616-ac8c-13a52487575b_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1007733,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07b488d5-7b67-4616-ac8c-13a52487575b_1024x559.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_!utTe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07b488d5-7b67-4616-ac8c-13a52487575b_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!utTe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07b488d5-7b67-4616-ac8c-13a52487575b_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!utTe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07b488d5-7b67-4616-ac8c-13a52487575b_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!utTe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07b488d5-7b67-4616-ac8c-13a52487575b_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If we do not simply underline nouns, how do we find candidate objects?</p><p>The answer is not to ignore domain language. Domain language matters. The problem is treating every noun as if it automatically deserves a class.</p><p>Candidate objects can come from many signals:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zXsJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdafb5571-9d6c-48e7-9461-17ef8af3cf1e_2748x789.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zXsJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdafb5571-9d6c-48e7-9461-17ef8af3cf1e_2748x789.png 424w, https://substackcdn.com/image/fetch/$s_!zXsJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdafb5571-9d6c-48e7-9461-17ef8af3cf1e_2748x789.png 848w, https://substackcdn.com/image/fetch/$s_!zXsJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdafb5571-9d6c-48e7-9461-17ef8af3cf1e_2748x789.png 1272w, https://substackcdn.com/image/fetch/$s_!zXsJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdafb5571-9d6c-48e7-9461-17ef8af3cf1e_2748x789.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zXsJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdafb5571-9d6c-48e7-9461-17ef8af3cf1e_2748x789.png" width="2748" height="789" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dafb5571-9d6c-48e7-9461-17ef8af3cf1e_2748x789.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:789,&quot;width&quot;:2748,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:182481,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23927ad1-d642-4255-8e2b-c1d9deb50e30_2800x942.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_!zXsJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdafb5571-9d6c-48e7-9461-17ef8af3cf1e_2748x789.png 424w, https://substackcdn.com/image/fetch/$s_!zXsJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdafb5571-9d6c-48e7-9461-17ef8af3cf1e_2748x789.png 848w, https://substackcdn.com/image/fetch/$s_!zXsJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdafb5571-9d6c-48e7-9461-17ef8af3cf1e_2748x789.png 1272w, https://substackcdn.com/image/fetch/$s_!zXsJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdafb5571-9d6c-48e7-9461-17ef8af3cf1e_2748x789.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RpK5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37de79c1-9b3b-400d-8d89-0f01c7ece77a_2723x764.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RpK5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37de79c1-9b3b-400d-8d89-0f01c7ece77a_2723x764.png 424w, https://substackcdn.com/image/fetch/$s_!RpK5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37de79c1-9b3b-400d-8d89-0f01c7ece77a_2723x764.png 848w, https://substackcdn.com/image/fetch/$s_!RpK5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37de79c1-9b3b-400d-8d89-0f01c7ece77a_2723x764.png 1272w, https://substackcdn.com/image/fetch/$s_!RpK5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37de79c1-9b3b-400d-8d89-0f01c7ece77a_2723x764.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RpK5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37de79c1-9b3b-400d-8d89-0f01c7ece77a_2723x764.png" width="1456" height="409" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/37de79c1-9b3b-400d-8d89-0f01c7ece77a_2723x764.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:409,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:117463,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37de79c1-9b3b-400d-8d89-0f01c7ece77a_2723x764.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_!RpK5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37de79c1-9b3b-400d-8d89-0f01c7ece77a_2723x764.png 424w, https://substackcdn.com/image/fetch/$s_!RpK5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37de79c1-9b3b-400d-8d89-0f01c7ece77a_2723x764.png 848w, https://substackcdn.com/image/fetch/$s_!RpK5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37de79c1-9b3b-400d-8d89-0f01c7ece77a_2723x764.png 1272w, https://substackcdn.com/image/fetch/$s_!RpK5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37de79c1-9b3b-400d-8d89-0f01c7ece77a_2723x764.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><pre><code><code>flowchart TD
    Scenario[Usage scenario] --&gt; Responsibilities
    DomainLanguage[Domain language] --&gt; CandidateConcepts[Candidate concepts]
    BusinessRules[Business rules] --&gt; PolicyObjects[Policy objects]
    ExternalSystems[External systems] --&gt; BoundaryObjects[Boundary objects]
    Relationships[Complex relationships] --&gt; Structurers
    Events[Domain events] --&gt; EventObjects

    Responsibilities --&gt; Candidates[Candidate objects]
    CandidateConcepts --&gt; Candidates
    PolicyObjects --&gt; Candidates
    BoundaryObjects --&gt; Candidates
    Structurers --&gt; Candidates
    EventObjects --&gt; Candidates

    Candidates --&gt; Keep[Keep]
    Candidates --&gt; Rename[Clarify]
    Candidates --&gt; Merge[Merge]
    Candidates --&gt; Reject[Reject]</code></code></pre><p>Finding candidates is only half the work.</p><p>The other half is rejecting weak ones.</p><p>A candidate object should be questioned if it only exists because a noun appeared in a requirement, if it only holds data and exposes getters, if its responsibility is vague, if it duplicates another object&#8217;s responsibility, or if it creates a boundary before the design needs one.</p><p>That does not mean small objects are bad. It means an object must earn its place by carrying meaning, behaviour, or collaboration value.</p><p>A useful question is:</p><blockquote><p>Would removing this object make the design less clear, or only shorter?</p></blockquote><p>If the design becomes clearer without it, the object may not be real yet.</p><h2>8. Assigning responsibilities</h2><p>Assigning responsibilities is the difficult part of object design.</p><p>It is easy to say that objects should have responsibilities. It is harder to decide where a responsibility belongs.</p><p>Responsibility-Driven Design (RDD) does not remove judgment, but it gives judgment better questions.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xMc5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71069dab-a867-44fc-88fe-8b6a8a84d014_2745x627.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xMc5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71069dab-a867-44fc-88fe-8b6a8a84d014_2745x627.png 424w, https://substackcdn.com/image/fetch/$s_!xMc5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71069dab-a867-44fc-88fe-8b6a8a84d014_2745x627.png 848w, https://substackcdn.com/image/fetch/$s_!xMc5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71069dab-a867-44fc-88fe-8b6a8a84d014_2745x627.png 1272w, https://substackcdn.com/image/fetch/$s_!xMc5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71069dab-a867-44fc-88fe-8b6a8a84d014_2745x627.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xMc5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71069dab-a867-44fc-88fe-8b6a8a84d014_2745x627.png" width="2745" height="627" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/71069dab-a867-44fc-88fe-8b6a8a84d014_2745x627.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:627,&quot;width&quot;:2745,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:199624,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0532b1a1-6692-4bf7-aa91-382c5d2042ee_2800x778.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_!xMc5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71069dab-a867-44fc-88fe-8b6a8a84d014_2745x627.png 424w, https://substackcdn.com/image/fetch/$s_!xMc5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71069dab-a867-44fc-88fe-8b6a8a84d014_2745x627.png 848w, https://substackcdn.com/image/fetch/$s_!xMc5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71069dab-a867-44fc-88fe-8b6a8a84d014_2745x627.png 1272w, https://substackcdn.com/image/fetch/$s_!xMc5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71069dab-a867-44fc-88fe-8b6a8a84d014_2745x627.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>One useful heuristic is &#8220;keep behaviour near the knowledge it needs&#8221;. But it must not be applied mechanically.</p><p>For example, we may try to put borrowing eligibility directly on <code>Member</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;1ff3fd16-179b-488b-b64a-bdea3601f2d2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">val borrowingDecision = member.canBorrow(
    activeLoans = activeLoans,
    requestedBookCopy = requestedBookCopy,
    today = today
)</code></pre></div><p>That may be reasonable if the decision is mostly about member state. But if eligibility depends on loan limits, membership type, book category, library branch policy, and temporary suspension rules, then <code>Member</code> may become overloaded.</p><p>A clearer responsibility home may be <code>BorrowingPolicy</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;aef90ae5-8bc4-4c2f-8962-d5ebbe7e4a92&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">val borrowingDecision = borrowingPolicy.assess(
    member = member,
    activeLoans = activeLoans,
    requestedBookCopy = requestedBookCopy,
    today = today
)</code></pre></div><p>This is not about extracting for the sake of extracting. It is about asking where the decision belongs.</p><p>The better heuristic is:</p><blockquote><p>Keep behaviour near the knowledge it needs, unless doing so gives the object responsibilities that do not belong to its role.</p></blockquote><p>That sentence is one of the most useful parts of Responsibility-Driven Design (RDD). It protects us from both extremes: passive data objects on one side and bloated domain objects on the other.</p><h2>9. Collaborations, messages, and contracts</h2><p>Responsibilities do not live in isolation.</p><p>An object fulfils a responsibility by doing its own work or by collaborating with others.</p><p>In Object-Oriented Programming (OOP), collaboration happens through messages. In most mainstream languages, that usually means method calls. But the design idea is larger than the syntax.</p><p>A message says:</p><blockquote><p>I need you to do something you are responsible for.</p></blockquote><p>A contract says:</p><blockquote><p>This is what you can expect when you ask me to do it.</p></blockquote><p>A protocol is the set of messages an object offers to its collaborators.</p><p>For example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;bdb5dc14-9da2-4f06-857e-69b2dd900bba&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface BorrowingPolicy {
    fun assess(
        member: LibraryMember,
        activeLoans: MemberLoans,
        requestedBookCopy: BookCopy,
        today: LocalDate
    ): BorrowingDecision
}</code></pre></div><p>For this example, we might read the contract like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rnvG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c07c751-8573-4aab-9288-2ae6f6a610cb_1434x596.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rnvG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c07c751-8573-4aab-9288-2ae6f6a610cb_1434x596.png 424w, https://substackcdn.com/image/fetch/$s_!rnvG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c07c751-8573-4aab-9288-2ae6f6a610cb_1434x596.png 848w, https://substackcdn.com/image/fetch/$s_!rnvG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c07c751-8573-4aab-9288-2ae6f6a610cb_1434x596.png 1272w, https://substackcdn.com/image/fetch/$s_!rnvG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c07c751-8573-4aab-9288-2ae6f6a610cb_1434x596.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rnvG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c07c751-8573-4aab-9288-2ae6f6a610cb_1434x596.png" width="1434" height="596" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c07c751-8573-4aab-9288-2ae6f6a610cb_1434x596.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:596,&quot;width&quot;:1434,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:108731,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30449a54-bf07-4298-b78b-3996f55789e7_1920x778.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_!rnvG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c07c751-8573-4aab-9288-2ae6f6a610cb_1434x596.png 424w, https://substackcdn.com/image/fetch/$s_!rnvG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c07c751-8573-4aab-9288-2ae6f6a610cb_1434x596.png 848w, https://substackcdn.com/image/fetch/$s_!rnvG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c07c751-8573-4aab-9288-2ae6f6a610cb_1434x596.png 1272w, https://substackcdn.com/image/fetch/$s_!rnvG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c07c751-8573-4aab-9288-2ae6f6a610cb_1434x596.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The name <code>BorrowingDecision</code> matters because it says what the object represents. It is not a generic response. It is the result of assessing whether borrowing is allowed.</p><p>A contract should avoid leaking implementation details into the client. <code>LendBookService</code> should not need to know the internal order of policy checks. It should ask for a decision and act on it.</p><p>That is encapsulation as design language.</p><p>Not just private fields.</p><p>A responsibility becomes concrete through a protocol. But the protocol should not be invented too early. First the role must be real. Then the contract must be meaningful. Then the interface can earn its place.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!C701!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffef5e6e0-b051-4fe6-8f1e-7c043140e290_2307x726.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!C701!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffef5e6e0-b051-4fe6-8f1e-7c043140e290_2307x726.png 424w, https://substackcdn.com/image/fetch/$s_!C701!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffef5e6e0-b051-4fe6-8f1e-7c043140e290_2307x726.png 848w, https://substackcdn.com/image/fetch/$s_!C701!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffef5e6e0-b051-4fe6-8f1e-7c043140e290_2307x726.png 1272w, https://substackcdn.com/image/fetch/$s_!C701!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffef5e6e0-b051-4fe6-8f1e-7c043140e290_2307x726.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!C701!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffef5e6e0-b051-4fe6-8f1e-7c043140e290_2307x726.png" width="1456" height="458" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fef5e6e0-b051-4fe6-8f1e-7c043140e290_2307x726.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:458,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:102331,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffef5e6e0-b051-4fe6-8f1e-7c043140e290_2307x726.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_!C701!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffef5e6e0-b051-4fe6-8f1e-7c043140e290_2307x726.png 424w, https://substackcdn.com/image/fetch/$s_!C701!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffef5e6e0-b051-4fe6-8f1e-7c043140e290_2307x726.png 848w, https://substackcdn.com/image/fetch/$s_!C701!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffef5e6e0-b051-4fe6-8f1e-7c043140e290_2307x726.png 1272w, https://substackcdn.com/image/fetch/$s_!C701!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffef5e6e0-b051-4fe6-8f1e-7c043140e290_2307x726.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><pre><code><code>sequenceDiagram
    participant LendBookService
    participant BorrowingPolicy
    participant LoanRepository
    participant LoanNotificationSender

    LendBookService-&gt;&gt;BorrowingPolicy: assess(member, activeLoans, bookCopy, today)
    BorrowingPolicy--&gt;&gt;LendBookService: BorrowingDecision
    LendBookService-&gt;&gt;LoanRepository: save(activeLoan)
    LendBookService-&gt;&gt;LoanNotificationSender: sendBookLentNotification(activeLoan)</code></code></pre><p>A sequence like this helps because it shows behaviour over time. It reveals who asks, who answers, and who is responsible for each part of the scenario.</p><h2>10. Class, Responsibility, Collaborator (CRC) cards</h2><p>Class, Responsibility, Collaborator (CRC) cards are useful because they force a simple but important conversation:</p><ul><li><p>What is this candidate responsible for?</p></li><li><p>What does it know?</p></li><li><p>What does it do?</p></li><li><p>Who does it collaborate with?</p></li></ul><p>They are not useful because the card is magical. They are useful because they make the design conversation visible.</p><p>A weak card might look like this:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TV7c!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2d23854-09ba-455e-aaf7-7637354a2e19_1857x377.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TV7c!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2d23854-09ba-455e-aaf7-7637354a2e19_1857x377.png 424w, https://substackcdn.com/image/fetch/$s_!TV7c!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2d23854-09ba-455e-aaf7-7637354a2e19_1857x377.png 848w, https://substackcdn.com/image/fetch/$s_!TV7c!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2d23854-09ba-455e-aaf7-7637354a2e19_1857x377.png 1272w, https://substackcdn.com/image/fetch/$s_!TV7c!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2d23854-09ba-455e-aaf7-7637354a2e19_1857x377.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TV7c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2d23854-09ba-455e-aaf7-7637354a2e19_1857x377.png" width="1857" height="377" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c2d23854-09ba-455e-aaf7-7637354a2e19_1857x377.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:377,&quot;width&quot;:1857,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:76591,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F950543b9-cba6-453d-bbeb-37b65621ecfa_1920x532.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_!TV7c!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2d23854-09ba-455e-aaf7-7637354a2e19_1857x377.png 424w, https://substackcdn.com/image/fetch/$s_!TV7c!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2d23854-09ba-455e-aaf7-7637354a2e19_1857x377.png 848w, https://substackcdn.com/image/fetch/$s_!TV7c!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2d23854-09ba-455e-aaf7-7637354a2e19_1857x377.png 1272w, https://substackcdn.com/image/fetch/$s_!TV7c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2d23854-09ba-455e-aaf7-7637354a2e19_1857x377.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>A better responsibility-driven version might look like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pMM-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9d3696a-3297-4b97-8757-b6ba69be1fa0_1858x595.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pMM-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9d3696a-3297-4b97-8757-b6ba69be1fa0_1858x595.png 424w, https://substackcdn.com/image/fetch/$s_!pMM-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9d3696a-3297-4b97-8757-b6ba69be1fa0_1858x595.png 848w, https://substackcdn.com/image/fetch/$s_!pMM-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9d3696a-3297-4b97-8757-b6ba69be1fa0_1858x595.png 1272w, https://substackcdn.com/image/fetch/$s_!pMM-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9d3696a-3297-4b97-8757-b6ba69be1fa0_1858x595.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pMM-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9d3696a-3297-4b97-8757-b6ba69be1fa0_1858x595.png" width="1858" height="595" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b9d3696a-3297-4b97-8757-b6ba69be1fa0_1858x595.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:595,&quot;width&quot;:1858,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:125553,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99c90cab-3eee-4c87-b1e7-d3206b0bedd5_1920x758.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_!pMM-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9d3696a-3297-4b97-8757-b6ba69be1fa0_1858x595.png 424w, https://substackcdn.com/image/fetch/$s_!pMM-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9d3696a-3297-4b97-8757-b6ba69be1fa0_1858x595.png 848w, https://substackcdn.com/image/fetch/$s_!pMM-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9d3696a-3297-4b97-8757-b6ba69be1fa0_1858x595.png 1272w, https://substackcdn.com/image/fetch/$s_!pMM-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9d3696a-3297-4b97-8757-b6ba69be1fa0_1858x595.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is still not a production ritual. It is a thinking tool.</p><p>The point is not to fill a card perfectly. The point is to ask whether an object has a real responsibility or whether we only gave a name to a thing.</p><p>https://emmanuelvalverderamos.substack.com/p/crc-cards-class-responsibility-collaborator</p><h2>11. Control style: centralized, dispersed, delegated</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vcwW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bb8df7d-7da1-4b93-bfc1-7015abfb7526_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vcwW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bb8df7d-7da1-4b93-bfc1-7015abfb7526_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!vcwW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bb8df7d-7da1-4b93-bfc1-7015abfb7526_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!vcwW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bb8df7d-7da1-4b93-bfc1-7015abfb7526_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!vcwW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bb8df7d-7da1-4b93-bfc1-7015abfb7526_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vcwW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bb8df7d-7da1-4b93-bfc1-7015abfb7526_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6bb8df7d-7da1-4b93-bfc1-7015abfb7526_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:891187,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bb8df7d-7da1-4b93-bfc1-7015abfb7526_1024x558.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_!vcwW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bb8df7d-7da1-4b93-bfc1-7015abfb7526_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!vcwW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bb8df7d-7da1-4b93-bfc1-7015abfb7526_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!vcwW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bb8df7d-7da1-4b93-bfc1-7015abfb7526_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!vcwW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bb8df7d-7da1-4b93-bfc1-7015abfb7526_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Responsibility distribution affects the control style of the system.</p><p>There are three useful styles to think about:</p><ol><li><p>Centralized control</p></li><li><p>Dispersed control</p></li><li><p>Delegated control</p></li></ol><p>A system can centralize control too much. In that style, one object knows the process, makes the decisions, tells everyone else what to do, and treats other objects mostly as data sources or technical endpoints. This may be easy to follow at first, but it often creates an object with centralized control, the kind of object many teams would casually call a God object.</p><p>A system can also disperse control too much. In that style, behaviour is spread everywhere, but without a clear sense of ownership. It becomes hard to know who is responsible for what. You avoid one dominant object, but you get a fog of tiny interactions where nobody can explain the flow.</p><p>The more useful middle is delegated control. A coordinator may still exist, but it does not do all the work. It delegates meaningful responsibilities to objects that are better homes for those responsibilities.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gN2s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0dba436-7a67-433b-9bf3-33b11182535c_1868x520.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gN2s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0dba436-7a67-433b-9bf3-33b11182535c_1868x520.png 424w, https://substackcdn.com/image/fetch/$s_!gN2s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0dba436-7a67-433b-9bf3-33b11182535c_1868x520.png 848w, https://substackcdn.com/image/fetch/$s_!gN2s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0dba436-7a67-433b-9bf3-33b11182535c_1868x520.png 1272w, https://substackcdn.com/image/fetch/$s_!gN2s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0dba436-7a67-433b-9bf3-33b11182535c_1868x520.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gN2s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0dba436-7a67-433b-9bf3-33b11182535c_1868x520.png" width="1868" height="520" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e0dba436-7a67-433b-9bf3-33b11182535c_1868x520.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:520,&quot;width&quot;:1868,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105528,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7906dba3-ae02-4183-b027-caefb49cf191_1920x676.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_!gN2s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0dba436-7a67-433b-9bf3-33b11182535c_1868x520.png 424w, https://substackcdn.com/image/fetch/$s_!gN2s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0dba436-7a67-433b-9bf3-33b11182535c_1868x520.png 848w, https://substackcdn.com/image/fetch/$s_!gN2s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0dba436-7a67-433b-9bf3-33b11182535c_1868x520.png 1272w, https://substackcdn.com/image/fetch/$s_!gN2s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0dba436-7a67-433b-9bf3-33b11182535c_1868x520.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8Hn7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6cb8844-3bc5-4501-9cc2-ab1260e830db_2849x1320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8Hn7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6cb8844-3bc5-4501-9cc2-ab1260e830db_2849x1320.png 424w, https://substackcdn.com/image/fetch/$s_!8Hn7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6cb8844-3bc5-4501-9cc2-ab1260e830db_2849x1320.png 848w, https://substackcdn.com/image/fetch/$s_!8Hn7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6cb8844-3bc5-4501-9cc2-ab1260e830db_2849x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!8Hn7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6cb8844-3bc5-4501-9cc2-ab1260e830db_2849x1320.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8Hn7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6cb8844-3bc5-4501-9cc2-ab1260e830db_2849x1320.png" width="1456" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b6cb8844-3bc5-4501-9cc2-ab1260e830db_2849x1320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:184193,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6cb8844-3bc5-4501-9cc2-ab1260e830db_2849x1320.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_!8Hn7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6cb8844-3bc5-4501-9cc2-ab1260e830db_2849x1320.png 424w, https://substackcdn.com/image/fetch/$s_!8Hn7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6cb8844-3bc5-4501-9cc2-ab1260e830db_2849x1320.png 848w, https://substackcdn.com/image/fetch/$s_!8Hn7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6cb8844-3bc5-4501-9cc2-ab1260e830db_2849x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!8Hn7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6cb8844-3bc5-4501-9cc2-ab1260e830db_2849x1320.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><pre><code><code>flowchart TD
    subgraph CentralizedControl[Centralized control]
        LendingServiceA[LendBookService] --&gt; MemberA[LibraryMember]
        LendingServiceA --&gt; LoansA[MemberLoans]
        LendingServiceA --&gt; BookCopyA[BookCopy]
        LendingServiceA --&gt; RepositoryA[LoanRepository]
        LendingServiceA --&gt; SenderA[LoanNotificationSender]
        LendingServiceA --&gt; CalendarA[Calendar]
    end

    subgraph DelegatedControl[Delegated control]
        LendingServiceB[LendBookService] --&gt; BorrowingPolicyB[BorrowingPolicy]
        BorrowingPolicyB --&gt; LoansB[MemberLoans]
        BorrowingPolicyB --&gt; BookCopyB[BookCopy]
        BorrowingPolicyB --&gt; MemberB[LibraryMember]
        LendingServiceB --&gt; RepositoryB[LoanRepository]
        LendingServiceB --&gt; SenderB[LoanNotificationSender]
        LendingServiceB --&gt; CalendarB[Calendar]
    end</code></code></pre><p>The difference is not that delegated control has more classes. The difference is that the work has a clearer shape.</p><p><code>LendBookService</code> coordinates.<br><code>BorrowingPolicy</code> decides.<br><code>ActiveLoan</code> represents the loan.<br><code>LoanRepository</code> persists.<br><code>LoanNotificationSender</code> notifies.</p><p>That is the goal: not maximum distribution, but meaningful distribution.</p><h2>12. A practical example in Kotlin</h2><p>Let us bring the ideas together in code.</p><p>Suppose we begin with this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;0a85571b-8ca2-4b51-a213-22df1cfa94d1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class LendBookService(
    private val loanRepository: LoanRepository,
    private val loanNotificationSender: LoanNotificationSender,
    private val calendar: Calendar
) {
    fun lendBook(
        member: LibraryMember,
        activeLoans: MemberLoans,
        requestedBookCopy: BookCopy
    ): ActiveLoan {
        val today = calendar.today()

        require(member.canBorrowBooks()) {
            "Member cannot borrow books."
        }

        require(activeLoans.count() &lt; member.maximumActiveLoans()) {
            "Member has reached the active loan limit."
        }

        require(requestedBookCopy.isAvailable()) {
            "Book copy is not available."
        }

        val loanPeriod = LoanPeriod.startingOn(today)
        val activeLoan = ActiveLoan.create(
            memberId = member.id,
            bookCopyId = requestedBookCopy.id,
            loanPeriod = loanPeriod
        )

        loanRepository.save(activeLoan)
        loanNotificationSender.sendBookLentNotification(activeLoan)

        return activeLoan
    }
}</code></pre></div><p>This code is not absurd. It may even be a reasonable first version.</p><p>But it has pressure.</p><p><code>LendBookService</code> coordinates the use case, checks member eligibility, checks loan limits, checks book availability, creates the loan period, saves the loan, and sends the notification. Some of that may belong there. Some of it may not.</p><p>A responsibility-driven question is:</p><blockquote><p>Is lending responsible for deciding borrowing eligibility, or is lending responsible for asking whether borrowing is allowed?</p></blockquote><p>That question lets us introduce a clearer responsibility:</p><blockquote><p>assess whether a member may borrow a requested book copy</p></blockquote><p>One possible home is <code>BorrowingPolicy</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;fb12f8fa-9a96-44f9-a3d2-d928d6e59a21&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class BorrowingPolicy(
    private val maximumActiveLoans: MaximumActiveLoans
) {
    fun assess(
        member: LibraryMember,
        activeLoans: MemberLoans,
        requestedBookCopy: BookCopy,
        today: LocalDate
    ): BorrowingDecision {
        if (!member.canBorrowBooks()) {
            return BorrowingDecision.rejected(
                reason = BorrowingRejectionReason.MemberCannotBorrow
            )
        }

        if (activeLoans.hasReached(maximumActiveLoans.forMember(member))) {
            return BorrowingDecision.rejected(
                reason = BorrowingRejectionReason.ActiveLoanLimitReached
            )
        }

        if (!requestedBookCopy.isAvailableOn(today)) {
            return BorrowingDecision.rejected(
                reason = BorrowingRejectionReason.BookCopyNotAvailable
            )
        }

        return BorrowingDecision.accepted()
    }
}</code></pre></div><p>Now <code>LendBookService</code> can coordinate at a higher level:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;6bff3798-5cff-439a-af51-b16baa2ac667&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class LendBookService(
    private val borrowingPolicy: BorrowingPolicy,
    private val loanRepository: LoanRepository,
    private val loanNotificationSender: LoanNotificationSender,
    private val calendar: Calendar
) {
    fun lendBook(
        member: LibraryMember,
        activeLoans: MemberLoans,
        requestedBookCopy: BookCopy
    ): LendingOutcome {
        val today = calendar.today()

        val borrowingDecision = borrowingPolicy.assess(
            member = member,
            activeLoans = activeLoans,
            requestedBookCopy = requestedBookCopy,
            today = today
        )

        if (borrowingDecision.isRejected()) {
            return LendingOutcome.rejected(borrowingDecision.rejectionReason())
        }

        val activeLoan = ActiveLoan.create(
            memberId = member.id,
            bookCopyId = requestedBookCopy.id,
            loanPeriod = LoanPeriod.startingOn(today)
        )

        loanRepository.save(activeLoan)
        loanNotificationSender.sendBookLentNotification(activeLoan)

        return LendingOutcome.accepted(activeLoan)
    }
}</code></pre></div><p>This is not a general rule that every condition must become a policy object. That would be another form of mechanical design.</p><p>The point is that a responsibility became visible. Once it became visible, we could ask where it belonged.</p><p>A responsibility-driven reading of the example might look like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Fncz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8af2ce94-f696-4fec-93d3-a35ad6b4a809_1867x949.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Fncz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8af2ce94-f696-4fec-93d3-a35ad6b4a809_1867x949.png 424w, https://substackcdn.com/image/fetch/$s_!Fncz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8af2ce94-f696-4fec-93d3-a35ad6b4a809_1867x949.png 848w, https://substackcdn.com/image/fetch/$s_!Fncz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8af2ce94-f696-4fec-93d3-a35ad6b4a809_1867x949.png 1272w, https://substackcdn.com/image/fetch/$s_!Fncz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8af2ce94-f696-4fec-93d3-a35ad6b4a809_1867x949.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Fncz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8af2ce94-f696-4fec-93d3-a35ad6b4a809_1867x949.png" width="1867" height="949" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8af2ce94-f696-4fec-93d3-a35ad6b4a809_1867x949.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:949,&quot;width&quot;:1867,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:206104,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F133207e0-6ef7-44bd-b2b2-1156be5ade12_1920x1106.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_!Fncz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8af2ce94-f696-4fec-93d3-a35ad6b4a809_1867x949.png 424w, https://substackcdn.com/image/fetch/$s_!Fncz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8af2ce94-f696-4fec-93d3-a35ad6b4a809_1867x949.png 848w, https://substackcdn.com/image/fetch/$s_!Fncz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8af2ce94-f696-4fec-93d3-a35ad6b4a809_1867x949.png 1272w, https://substackcdn.com/image/fetch/$s_!Fncz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8af2ce94-f696-4fec-93d3-a35ad6b4a809_1867x949.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tghK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6702ff36-1512-4502-b656-5bd00d5d6b95_2511x556.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tghK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6702ff36-1512-4502-b656-5bd00d5d6b95_2511x556.png 424w, https://substackcdn.com/image/fetch/$s_!tghK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6702ff36-1512-4502-b656-5bd00d5d6b95_2511x556.png 848w, https://substackcdn.com/image/fetch/$s_!tghK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6702ff36-1512-4502-b656-5bd00d5d6b95_2511x556.png 1272w, https://substackcdn.com/image/fetch/$s_!tghK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6702ff36-1512-4502-b656-5bd00d5d6b95_2511x556.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tghK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6702ff36-1512-4502-b656-5bd00d5d6b95_2511x556.png" width="1456" height="322" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6702ff36-1512-4502-b656-5bd00d5d6b95_2511x556.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:322,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:79910,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6702ff36-1512-4502-b656-5bd00d5d6b95_2511x556.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_!tghK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6702ff36-1512-4502-b656-5bd00d5d6b95_2511x556.png 424w, https://substackcdn.com/image/fetch/$s_!tghK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6702ff36-1512-4502-b656-5bd00d5d6b95_2511x556.png 848w, https://substackcdn.com/image/fetch/$s_!tghK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6702ff36-1512-4502-b656-5bd00d5d6b95_2511x556.png 1272w, https://substackcdn.com/image/fetch/$s_!tghK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6702ff36-1512-4502-b656-5bd00d5d6b95_2511x556.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><pre><code><code>flowchart TD
    LendBookService --&gt; Calendar
    LendBookService --&gt; BorrowingPolicy
    BorrowingPolicy --&gt; LibraryMember
    BorrowingPolicy --&gt; MemberLoans
    BorrowingPolicy --&gt; BookCopy
    LendBookService --&gt; ActiveLoan
    ActiveLoan --&gt; LoanPeriod
    LendBookService --&gt; LoanRepository
    LendBookService --&gt; LoanNotificationSender</code></code></pre><p>The design now says something clearer.</p><p>The service coordinates the lending scenario. The policy decides whether the loan is allowed. The repository persists. The sender notifies. The values and information holders carry domain meaning.</p><p>That is Responsibility-Driven Design (RDD) in practice.</p><p>Not &#8220;make more classes&#8221;.</p><p>Not &#8220;hide everything behind interfaces&#8221;.</p><p>Not &#8220;avoid coordination&#8221;.</p><p>The lesson is simpler:</p><blockquote><p>Give behaviour a responsible home.</p></blockquote><h2>13. Trust boundaries and reliable collaborations</h2><p>Not all collaborations are equal.</p><p>Some collaborations happen inside a trusted part of the model. Others cross boundaries where failure is normal.</p><p>This distinction matters because object-oriented design is not only about who talks to whom. It is also about how much trust is reasonable in that conversation.</p><p>In this simplified example, a collaboration between <code>LendBookService</code> and <code>BorrowingPolicy</code> can be designed with higher trust because both objects live inside the application model. If the domain values are valid, <code>BorrowingPolicy</code> can focus on the borrowing decision.</p><p>A collaboration between <code>LendBookService</code> and <code>LoanRepository</code> is different. Persistence can fail. A database may be unavailable. A write may conflict. A transaction may be interrupted.</p><p>A collaboration with notification is also different. An email provider can fail. A message can be delayed. A downstream consumer may be unavailable.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6PKE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4c948dd-6a03-44e0-9eb0-89812b122a63_1659x1166.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6PKE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4c948dd-6a03-44e0-9eb0-89812b122a63_1659x1166.png 424w, https://substackcdn.com/image/fetch/$s_!6PKE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4c948dd-6a03-44e0-9eb0-89812b122a63_1659x1166.png 848w, https://substackcdn.com/image/fetch/$s_!6PKE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4c948dd-6a03-44e0-9eb0-89812b122a63_1659x1166.png 1272w, https://substackcdn.com/image/fetch/$s_!6PKE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4c948dd-6a03-44e0-9eb0-89812b122a63_1659x1166.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6PKE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4c948dd-6a03-44e0-9eb0-89812b122a63_1659x1166.png" width="1456" height="1023" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a4c948dd-6a03-44e0-9eb0-89812b122a63_1659x1166.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1023,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:98270,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4c948dd-6a03-44e0-9eb0-89812b122a63_1659x1166.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_!6PKE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4c948dd-6a03-44e0-9eb0-89812b122a63_1659x1166.png 424w, https://substackcdn.com/image/fetch/$s_!6PKE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4c948dd-6a03-44e0-9eb0-89812b122a63_1659x1166.png 848w, https://substackcdn.com/image/fetch/$s_!6PKE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4c948dd-6a03-44e0-9eb0-89812b122a63_1659x1166.png 1272w, https://substackcdn.com/image/fetch/$s_!6PKE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4c948dd-6a03-44e0-9eb0-89812b122a63_1659x1166.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><pre><code><code>flowchart LR
    subgraph ApplicationModel[Application model]
        LendBookService
        BorrowingPolicy
        ActiveLoan
        LoanPeriod
    end

    subgraph ExternalBoundaries[External boundaries]
        Database[(Database)]
        NotificationChannel[Notification channel]
    end

    LendBookService --&gt; BorrowingPolicy
    LendBookService --&gt; LoanRepository --&gt; Database
    LendBookService --&gt; LoanNotificationSender --&gt; NotificationChannel</code></code></pre><p>A table makes the difference visible:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!S9Cm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa02864-1139-4d7a-a0fc-6124a7eb2ec5_1860x593.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!S9Cm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa02864-1139-4d7a-a0fc-6124a7eb2ec5_1860x593.png 424w, https://substackcdn.com/image/fetch/$s_!S9Cm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa02864-1139-4d7a-a0fc-6124a7eb2ec5_1860x593.png 848w, https://substackcdn.com/image/fetch/$s_!S9Cm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa02864-1139-4d7a-a0fc-6124a7eb2ec5_1860x593.png 1272w, https://substackcdn.com/image/fetch/$s_!S9Cm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa02864-1139-4d7a-a0fc-6124a7eb2ec5_1860x593.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!S9Cm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa02864-1139-4d7a-a0fc-6124a7eb2ec5_1860x593.png" width="1860" height="593" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/caa02864-1139-4d7a-a0fc-6124a7eb2ec5_1860x593.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:593,&quot;width&quot;:1860,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:130390,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c28c81a-f17a-482c-9262-0c91471e993b_1920x758.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_!S9Cm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa02864-1139-4d7a-a0fc-6124a7eb2ec5_1860x593.png 424w, https://substackcdn.com/image/fetch/$s_!S9Cm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa02864-1139-4d7a-a0fc-6124a7eb2ec5_1860x593.png 848w, https://substackcdn.com/image/fetch/$s_!S9Cm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa02864-1139-4d7a-a0fc-6124a7eb2ec5_1860x593.png 1272w, https://substackcdn.com/image/fetch/$s_!S9Cm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcaa02864-1139-4d7a-a0fc-6124a7eb2ec5_1860x593.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Reliability is responsibility allocation under uncertainty.</p><p>It is not enough to say that persistence can fail. We need to decide who notices the failure, who translates it, who decides what it means, and who takes recovery action.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wshn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017e90d6-76dc-475d-ab30-88436f23b730_1868x647.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wshn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017e90d6-76dc-475d-ab30-88436f23b730_1868x647.png 424w, https://substackcdn.com/image/fetch/$s_!wshn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017e90d6-76dc-475d-ab30-88436f23b730_1868x647.png 848w, https://substackcdn.com/image/fetch/$s_!wshn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017e90d6-76dc-475d-ab30-88436f23b730_1868x647.png 1272w, https://substackcdn.com/image/fetch/$s_!wshn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017e90d6-76dc-475d-ab30-88436f23b730_1868x647.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wshn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017e90d6-76dc-475d-ab30-88436f23b730_1868x647.png" width="1868" height="647" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/017e90d6-76dc-475d-ab30-88436f23b730_1868x647.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:647,&quot;width&quot;:1868,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:140006,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fe1bf23-e692-47b3-9fc3-d6c8348caa60_1920x806.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_!wshn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017e90d6-76dc-475d-ab30-88436f23b730_1868x647.png 424w, https://substackcdn.com/image/fetch/$s_!wshn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017e90d6-76dc-475d-ab30-88436f23b730_1868x647.png 848w, https://substackcdn.com/image/fetch/$s_!wshn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017e90d6-76dc-475d-ab30-88436f23b730_1868x647.png 1272w, https://substackcdn.com/image/fetch/$s_!wshn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017e90d6-76dc-475d-ab30-88436f23b730_1868x647.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The point is not to make every object defensive against everything. That creates duplication and noise.</p><p>The point is to decide where trust is reasonable and where failure deserves explicit responsibility.</p><h2>14. Flexibility and variation points</h2><p>Flexibility is valuable, but it is not free.</p><p>Many object-oriented designs become over-engineered because every collaborator receives an interface, every rule becomes a strategy, and every small calculation becomes an object with a name. The design looks flexible, but the flexibility is speculative. The cost is paid immediately. The benefit may not arrive.</p><p>Responsibility-Driven Design (RDD) gives us a better question:</p><blockquote><p>Where is variation real enough to deserve a design boundary?</p></blockquote><p>In the library example, some variation points are plausible.</p><p>Borrowing rules may vary by membership type. Loan periods may vary by book category. Notification channels may vary. Persistence mechanisms may vary. Holiday calendars may affect due dates.</p><p>Other things may not deserve flexibility yet.</p><p>A method that calculates whether one date is after another probably does not need an interface. A small value like <code>BookCopyId</code> is not a variation point. A simple <code>LoanPeriod</code> may not need a hierarchy.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YwKO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54e2ff72-e6af-4bd7-a55e-9a39f5bd3990_1869x614.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YwKO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54e2ff72-e6af-4bd7-a55e-9a39f5bd3990_1869x614.png 424w, https://substackcdn.com/image/fetch/$s_!YwKO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54e2ff72-e6af-4bd7-a55e-9a39f5bd3990_1869x614.png 848w, https://substackcdn.com/image/fetch/$s_!YwKO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54e2ff72-e6af-4bd7-a55e-9a39f5bd3990_1869x614.png 1272w, https://substackcdn.com/image/fetch/$s_!YwKO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54e2ff72-e6af-4bd7-a55e-9a39f5bd3990_1869x614.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YwKO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54e2ff72-e6af-4bd7-a55e-9a39f5bd3990_1869x614.png" width="1869" height="614" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54e2ff72-e6af-4bd7-a55e-9a39f5bd3990_1869x614.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:614,&quot;width&quot;:1869,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:130847,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ec91667-ec61-4fe0-b4fb-98fa4bbca6bc_1920x778.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_!YwKO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54e2ff72-e6af-4bd7-a55e-9a39f5bd3990_1869x614.png 424w, https://substackcdn.com/image/fetch/$s_!YwKO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54e2ff72-e6af-4bd7-a55e-9a39f5bd3990_1869x614.png 848w, https://substackcdn.com/image/fetch/$s_!YwKO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54e2ff72-e6af-4bd7-a55e-9a39f5bd3990_1869x614.png 1272w, https://substackcdn.com/image/fetch/$s_!YwKO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54e2ff72-e6af-4bd7-a55e-9a39f5bd3990_1869x614.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yvkf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F063336bf-4105-4cae-8c90-da0bb9af077a_1400x1964.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yvkf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F063336bf-4105-4cae-8c90-da0bb9af077a_1400x1964.png 424w, https://substackcdn.com/image/fetch/$s_!yvkf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F063336bf-4105-4cae-8c90-da0bb9af077a_1400x1964.png 848w, https://substackcdn.com/image/fetch/$s_!yvkf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F063336bf-4105-4cae-8c90-da0bb9af077a_1400x1964.png 1272w, https://substackcdn.com/image/fetch/$s_!yvkf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F063336bf-4105-4cae-8c90-da0bb9af077a_1400x1964.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yvkf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F063336bf-4105-4cae-8c90-da0bb9af077a_1400x1964.png" width="1400" height="1964" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/063336bf-4105-4cae-8c90-da0bb9af077a_1400x1964.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1964,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:132561,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F063336bf-4105-4cae-8c90-da0bb9af077a_1400x1964.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_!yvkf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F063336bf-4105-4cae-8c90-da0bb9af077a_1400x1964.png 424w, https://substackcdn.com/image/fetch/$s_!yvkf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F063336bf-4105-4cae-8c90-da0bb9af077a_1400x1964.png 848w, https://substackcdn.com/image/fetch/$s_!yvkf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F063336bf-4105-4cae-8c90-da0bb9af077a_1400x1964.png 1272w, https://substackcdn.com/image/fetch/$s_!yvkf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F063336bf-4105-4cae-8c90-da0bb9af077a_1400x1964.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><pre><code><code>flowchart TD
    VariationPoint[Potential variation point] --&gt; ConcreteExamples{Do we have concrete variations?}
    ConcreteExamples --&gt;|No| KeepSimple[Keep the design simple]
    ConcreteExamples --&gt;|Yes| Impact{Does variation affect many clients?}
    Impact --&gt;|No| Localize[Keep variation local]
    Impact --&gt;|Yes| Boundary[Introduce explicit boundary]
    Boundary --&gt; Contract[Define contract]
</code></code></pre><p>Patterns fit here, but not as decoration.</p><p>A pattern is useful when it expresses a better distribution of responsibilities.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RqXq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8973884-57f0-4609-a78c-ef36c79de1f7_1351x625.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RqXq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8973884-57f0-4609-a78c-ef36c79de1f7_1351x625.png 424w, https://substackcdn.com/image/fetch/$s_!RqXq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8973884-57f0-4609-a78c-ef36c79de1f7_1351x625.png 848w, https://substackcdn.com/image/fetch/$s_!RqXq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8973884-57f0-4609-a78c-ef36c79de1f7_1351x625.png 1272w, https://substackcdn.com/image/fetch/$s_!RqXq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8973884-57f0-4609-a78c-ef36c79de1f7_1351x625.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RqXq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8973884-57f0-4609-a78c-ef36c79de1f7_1351x625.png" width="1351" height="625" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f8973884-57f0-4609-a78c-ef36c79de1f7_1351x625.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:625,&quot;width&quot;:1351,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:115481,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa09df7a6-1aaf-4f7e-9875-a8bc3db278d4_1920x778.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_!RqXq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8973884-57f0-4609-a78c-ef36c79de1f7_1351x625.png 424w, https://substackcdn.com/image/fetch/$s_!RqXq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8973884-57f0-4609-a78c-ef36c79de1f7_1351x625.png 848w, https://substackcdn.com/image/fetch/$s_!RqXq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8973884-57f0-4609-a78c-ef36c79de1f7_1351x625.png 1272w, https://substackcdn.com/image/fetch/$s_!RqXq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8973884-57f0-4609-a78c-ef36c79de1f7_1351x625.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The pattern is justified only when the responsibility movement is justified.</p><p>Good design is not maximum abstraction. Good design is abstraction where it pays rent.</p><h2>15. Patterns as responsibility arrangements</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i-FC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b100ef3-3eef-4e24-b742-c48087934fc3_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i-FC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b100ef3-3eef-4e24-b742-c48087934fc3_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!i-FC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b100ef3-3eef-4e24-b742-c48087934fc3_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!i-FC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b100ef3-3eef-4e24-b742-c48087934fc3_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!i-FC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b100ef3-3eef-4e24-b742-c48087934fc3_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i-FC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b100ef3-3eef-4e24-b742-c48087934fc3_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0b100ef3-3eef-4e24-b742-c48087934fc3_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:725666,&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://emmanuelvalverderamos.substack.com/i/195625459?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b100ef3-3eef-4e24-b742-c48087934fc3_1024x558.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_!i-FC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b100ef3-3eef-4e24-b742-c48087934fc3_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!i-FC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b100ef3-3eef-4e24-b742-c48087934fc3_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!i-FC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b100ef3-3eef-4e24-b742-c48087934fc3_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!i-FC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b100ef3-3eef-4e24-b742-c48087934fc3_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Patterns deserve their own short reflection because they are often taught badly.</p><p>Many developers learn patterns as shapes. They recognize a class diagram, remember a name, and then look for a place to apply it. That is backwards.</p><p>From a Responsibility-Driven Design (RDD) perspective, a pattern is more useful when read as an arrangement of responsibilities.</p><p>Strategy is not &#8220;an interface with implementations&#8221;. It is a way to give a varying decision to interchangeable collaborators.</p><p>Observer is not &#8220;a list of subscribers&#8221;. It is a way to separate the fact that something happened from the objects that react to it.</p><p>Adapter is not &#8220;a wrapper&#8221;. It is a way to protect the model from a collaborator whose protocol does not fit the design we want.</p><p>State is not &#8220;classes for states&#8221;. It is a way to move state-dependent behaviour into the objects that represent those states.</p><p>That perspective is important because it prevents pattern worship. The pattern is not the goal. The responsibility movement is the goal.</p><p>In the library example, <code>BorrowingPolicy</code> might evolve into a strategy if borrowing rules vary by member type, branch, or material type. <code>LoanNotificationSender</code> might evolve toward an observer-like design if many parts of the system need to react to a <code>BookLent</code> event. <code>LoanRepository</code> may use an adapter internally to protect the application from a persistence framework.</p><p>But none of those choices should be automatic.</p><p>First understand the responsibility. Then understand the collaboration. Then choose the structure that expresses them well.</p><h2>16. Conclusion</h2><p>Responsibility-Driven Design (RDD) is not about collecting classes.</p><p>It is a way of thinking about object-oriented software as a community of responsible collaborators.</p><p>That changes the starting point. We do not begin with data and then attach behaviour. We begin with behaviour and ask where responsibility belongs. We do not reduce objects to containers. We treat them as participants. We do not centralize every decision in one smart service. We distribute intelligence across objects that know how to do their work.</p><p>We do not use Class, Responsibility, Collaborator (CRC) cards because cards are magic. We use them because they make responsibilities and collaborations visible. We do not delegate because indirection is good. We delegate because another object is a better home for the work.</p><p>We do not add flexibility everywhere. We add it where variation is real. We do not add interfaces because classes exist. We add boundaries when a role, a contract, or a trust boundary earns one. We do not use patterns to decorate code. We use patterns when they express a better distribution of responsibilities.</p><p>Good Object-Oriented Programming (OOP) is not about having more classes, more interfaces, or more pattern vocabulary. It is about making the system easier to understand because responsibilities are named, behaviour has a home, collaboration has a shape, and control lives where it belongs.</p><p>The best object designs do not feel like a pile of classes.</p><p>They feel like a community of objects that know what they are responsible for and know when to ask each other for help.</p><h2>References</h2><h3>Books, papers, and talks</h3><ul><li><p>Beck, Kent; Cunningham, Ward. <em>A Laboratory for Teaching Object-Oriented Thinking</em>.<br><a href="https://c2.com/doc/oopsla89/paper.html">https://c2.com/doc/oopsla89/paper.html</a></p></li><li><p>Cooper, Ian. <em>Responsibility Driven Design Revisited</em>.</p></li></ul><div id="youtube2-xOpUpRIpH6k" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;xOpUpRIpH6k&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/xOpUpRIpH6k?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><ul><li><p>Freeman, Steve; Pryce, Nat. <em>Growing Object-Oriented Software, Guided by Tests</em>.</p><p><a href="https://www.growing-object-oriented-software.com/">https://www.growing-object-oriented-software.com/</a></p></li><li><p>West, David. <em>Object Thinking</em>.<br><a href="https://www.microsoftpressstore.com/store/object-thinking-9780735619654">https://www.microsoftpressstore.com/store/object-thinking-9780735619654</a></p></li><li><p>Wirfs-Brock, Rebecca. <em>Object Technology: Basic Concepts</em>.<br><a href="https://www.informit.com/articles/article.aspx?p=3109983">https://www.informit.com/articles/article.aspx?p=3109983</a></p></li><li><p>Wirfs-Brock, Rebecca. <em>Responsibility-Driven Design</em>.<br><a href="https://www.wirfs-brock.com/Design.html">https://www.wirfs-brock.com/Design.html</a></p></li><li><p>Wirfs-Brock, Rebecca; McKean, Alan. <em>Object Design: Roles, Responsibilities, and Collaborations</em>.<br><a href="https://www.informit.com/store/object-design-roles-responsibilities-and-collaborations-9780201379433">https://www.informit.com/store/object-design-roles-responsibilities-and-collaborations-9780201379433</a></p></li><li><p>Wirfs-Brock, Rebecca; Wilkerson, Brian; Wiener, Lauren. <em>Designing Object-Oriented Software</em>.<br><a href="https://archive.org/details/designingobjecto00wirf">https://archive.org/details/designingobjecto00wirf</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Role tests are not about comparing implementations 2]]></title><description><![CDATA[How role tests turn interfaces from method shapes into executable behavioural contracts]]></description><link>https://emmanuelvalverderamos.substack.com/p/role-tests-are-not-about-comparing-c75</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/role-tests-are-not-about-comparing-c75</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 30 Jun 2026 06:01:41 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7PZL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Disclaimer</h2><p>This article is a continuation or extension of the article</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;740d9cfd-308a-4ed4-9a6e-e2a51cfd203e&quot;,&quot;caption&quot;:&quot;Interfaces often give us confidence earlier than they should.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Role tests are not about comparing implementations&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-06-23T05:00:35.953Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!JAKb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/role-tests-are-not-about-comparing&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:195733924,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:4,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Following these two points of clarification:</p><ul><li><p>Role testing is not contract testing: In the previous article, Javi L&#243;pez mentioned to me that he does not view role testing as contract testing.</p></li><li><p>My friend Adr&#237;an Mu&#241;oz Gamez gave me feedback that in the previous article Tests should fail for only one reason, for the sake of simplicity, I wrote a test that called `save` and then `findById`, which is not a good or consistent test, since it should fail for only one reason, and that test had two reasons to fail.</p></li></ul><p>Therefore, to clarify these questions and delve deeper into the topic, I have written this article.</p><h2>Introduction</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7PZL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7PZL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!7PZL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!7PZL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!7PZL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7PZL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:878391,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.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_!7PZL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!7PZL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!7PZL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!7PZL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a63d7a4-2bc0-4acd-857c-a59d62545e3e_1024x559.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Interfaces often give us confidence earlier than they should.</p><p>At first, everything looks fine. The interface is clean. One implementation passes its tests. Another is convenient for local development. A third might be useful in tests or in a different runtime context. The compiler is satisfied, the test suite is green, and the boundary appears well designed.</p><p>None of those facts proves that every implementation means the same thing by <code>save</code> and <code>findById</code>.</p><p>Consider these two implementations:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;8c0a9674-7849-4836-b865-4a2011c43a9e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class InMemoryCustomerRepository : CustomerRepository {
    private val customers = mutableMapOf&lt;CustomerId, Customer&gt;()

    override fun save(customer: Customer) {
        customers[customer.id] = customer
    }

    // ...
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;ff4003f7-ba8d-4704-931a-3931701b72e8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class PostgresCustomerRepository(
    private val rows: CustomerRows
) : CustomerRepository {

    override fun save(customer: Customer) {
        rows.insert(
            CustomerRow(
                id = customer.id.value,
                name = customer.name
            )
        )
    }

    // ...
}</code></pre></div><p><code>CustomerRows</code> is a small table gateway used to keep the example focused on repository behaviour rather than database driver calls. Its shape appears later.</p><p>Both implement the same method:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;27b7d2ec-d6e6-4c44-b6de-a19bd895ac13&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">fun save(customer: Customer)</code></pre></div><p>A basic test that saves one customer and finds it later can pass for both. But save the same customer id twice and their meanings separate.</p><p>The in-memory implementation replaces the previous value. Assuming <code>id</code> is a primary key, the PostgreSQL implementation shown above rejects the second insert. One interprets <code>save</code> as an upsert. The other interprets it as an insert.</p><p>The interface did not prevent the disagreement:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;c1aa7821-a199-4d4e-b80c-26fcd2bfd45a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerRepository {
    fun findById(id: CustomerId): CustomerLookup
    fun save(customer: Customer)
}</code></pre></div><p>The same structural interface can therefore hide incompatible observable behaviour:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-ZBU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa483a202-5c1d-4b3c-a4fb-e3d22d95a554_3014x444.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-ZBU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa483a202-5c1d-4b3c-a4fb-e3d22d95a554_3014x444.png 424w, https://substackcdn.com/image/fetch/$s_!-ZBU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa483a202-5c1d-4b3c-a4fb-e3d22d95a554_3014x444.png 848w, https://substackcdn.com/image/fetch/$s_!-ZBU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa483a202-5c1d-4b3c-a4fb-e3d22d95a554_3014x444.png 1272w, https://substackcdn.com/image/fetch/$s_!-ZBU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa483a202-5c1d-4b3c-a4fb-e3d22d95a554_3014x444.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-ZBU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa483a202-5c1d-4b3c-a4fb-e3d22d95a554_3014x444.png" width="1456" height="214" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a483a202-5c1d-4b3c-a4fb-e3d22d95a554_3014x444.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:214,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:93525,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa483a202-5c1d-4b3c-a4fb-e3d22d95a554_3014x444.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_!-ZBU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa483a202-5c1d-4b3c-a4fb-e3d22d95a554_3014x444.png 424w, https://substackcdn.com/image/fetch/$s_!-ZBU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa483a202-5c1d-4b3c-a4fb-e3d22d95a554_3014x444.png 848w, https://substackcdn.com/image/fetch/$s_!-ZBU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa483a202-5c1d-4b3c-a4fb-e3d22d95a554_3014x444.png 1272w, https://substackcdn.com/image/fetch/$s_!-ZBU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa483a202-5c1d-4b3c-a4fb-e3d22d95a554_3014x444.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption"><a href="https://mermaid.live/edit#pako:eNptkTtPwzAQgP_K6SaQ2qEdM7CUpVKrvpggDEdySSz5Ec52Ear637mkLQjE5rO_7x6-E1ahZiywseGj6kgSrPalB1i8lLiwhn2CiqyNEOnId1WOKTiW-xJfYTp9gOWAXS_33IdoUpBPMD6xNFSxckO25QivFV76qWM3Mq637LQAJRP8L3Cr4DbE1Aofdqv_yfVIbmaKHrgKvh47BOHeat0IqWO4tXtVthdl_kc5mmApqUKQvXnP7DlG0PeYhHSSq72ZjfpO7SfN_T0imAjOxGh8qxneuCNNmIUsSLa3D9jMLzJOsBVTY5Ek8wS1N0dDiKeBKlG7duoUeqy5oWy1eunPqvXkn0NwN1NCbjssGrJRo9zXOsGjoVboB2FfsyxC9gmL2QS5Hpazvux7XPv5C2KZrDI">Open and edit this diagram in Mermaid Live</a></figcaption></figure></div><p>It defines which messages clients can send. It does not define enough of the behaviour clients can rely on.</p><p>That is the problem role tests help us examine.</p><p>A role test is a reusable suite of behavioural examples for a role. It does not primarily ask whether the in-memory repository behaves like PostgreSQL, or whether a new implementation reproduces every observable detail of an old one.</p><p>A role contract asks:</p><blockquote><p>What must every implementation make true so that clients can safely depend on this role?</p></blockquote><p>Each implementation is checked against the same behavioural specification.</p><p>The contract boundary is:</p><blockquote><p>A role test does not compare implementations with one another. It checks each implementation against the client-visible behavioural contract of the role.</p></blockquote><p>This makes role tests useful in several situations:</p><ul><li><p>Keeping an in-memory fake aligned with a production adapter</p></li><li><p>Checking several production adapters that claim the same role</p></li><li><p>Introducing a new Strategy implementation</p></li><li><p>Replacing a library, database adapter, algorithm, or external dependency during a refactor</p></li><li><p>Running an old and a new implementation side by side while clients migrate</p></li></ul><p>There is also an important limit. Passing a finite test suite does not prove complete substitutability. It provides executable evidence for the behaviours represented by those examples. Untested concurrency, durability, performance, security, and failure properties do not become guaranteed merely because the suite is green.</p><p>Role tests are therefore both a testing technique and a design technique. They protect known behaviour, expose semantic disagreements, keep test doubles honest, and force vague abstractions to become more precise.</p><h2>TL;DR</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xt9e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9469d067-e73a-4286-86e1-7872a40f2426_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xt9e!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9469d067-e73a-4286-86e1-7872a40f2426_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!xt9e!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9469d067-e73a-4286-86e1-7872a40f2426_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!xt9e!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9469d067-e73a-4286-86e1-7872a40f2426_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!xt9e!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9469d067-e73a-4286-86e1-7872a40f2426_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xt9e!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9469d067-e73a-4286-86e1-7872a40f2426_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9469d067-e73a-4286-86e1-7872a40f2426_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1051182,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9469d067-e73a-4286-86e1-7872a40f2426_1024x559.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_!xt9e!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9469d067-e73a-4286-86e1-7872a40f2426_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!xt9e!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9469d067-e73a-4286-86e1-7872a40f2426_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!xt9e!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9469d067-e73a-4286-86e1-7872a40f2426_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!xt9e!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9469d067-e73a-4286-86e1-7872a40f2426_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p><strong>An interface defines shape, while a role test defines expected behaviour.</strong> A signature can say <code>save(customer)</code>, but it cannot fully say what must be true after saving</p></li><li><p><strong>Role tests do not primarily compare implementations.</strong> They run the same behavioural specification against each implementation</p></li><li><p><strong>In object-oriented literature, role tests are a form of contract or polymorphic testing.</strong> The name emphasizes that one behavioural contract is reused against every implementation of a role</p></li><li><p><strong>The role is the reference, not the fake, the database, or the legacy implementation.</strong> When implementations disagree, the team must clarify the contract</p></li><li><p><strong>Roles are often discovered from the client side.</strong> A client&#8217;s needs reveal the messages and responsibilities required from a collaborator</p></li><li><p><strong>Role tests are valuable during refactors.</strong> An old dependency and its replacement can both be adapted to the same role and checked with the same suite before clients migrate</p></li><li><p><strong>Role tests keep fakes aligned with real adapters.</strong> A lightweight in-memory implementation should satisfy the same client-visible promises, even though it does not reproduce infrastructure details</p></li><li><p><strong>Strategy implementations can also have role tests.</strong> The shared suite protects properties common to every valid strategy, or the complete behaviour when a new strategy is intended as a drop-in replacement</p></li><li><p><strong>Passing the suite is evidence, not a formal proof.</strong> The suite protects only the part of the contract it encodes</p></li><li><p><strong>Implementation-specific tests still matter.</strong> SQL, filesystem, HTTP, security, performance, and operational concerns need their own tests</p></li></ul><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Table of contents</h2><ul><li><p><a href="?open=false#&#167;the-problem-with-interfaces">The problem with interfaces</a></p></li><li><p><a href="?open=false#&#167;what-a-role-test-is">What a role test is</a></p></li><li><p><a href="?open=false#&#167;are-role-tests-contract-tests">Are role tests contract tests?</a></p></li><li><p><a href="?open=false#&#167;what-a-role-test-is-not">What a role test is not</a></p></li><li><p><a href="?open=false#&#167;roles-are-discovered-from-the-client-side">Roles are discovered from the client side</a></p></li><li><p><a href="?open=false#&#167;a-small-example-in-kotlin">A small example in Kotlin</a></p></li><li><p><a href="?open=false#&#167;the-first-testing-smell-using-one-role-method-to-arrange-another">The first testing smell: using one role method to arrange another</a></p></li><li><p><a href="?open=false#&#167;a-precise-role-test-fixture">A precise role-test fixture</a></p></li><li><p><a href="?open=false#&#167;testing-two-implementations-with-the-same-role-test">Testing two implementations with the same role test</a></p></li><li><p><a href="?open=false#&#167;the-missing-save-contract">The missing save contract</a></p></li><li><p><a href="?open=false#&#167;keeping-test-doubles-aligned-with-real-adapters">Keeping test doubles aligned with real adapters</a></p></li><li><p><a href="?open=false#&#167;using-role-tests-to-replace-a-dependency-safely">Using role tests to replace a dependency safely</a></p></li><li><p><a href="?open=false#&#167;replacing-an-openapi-generated-client-without-breaking-local-clients">Replacing an OpenAPI-generated client without breaking local clients</a></p></li><li><p><a href="?open=false#&#167;a-strategy-example-during-a-refactor">A Strategy example during a refactor</a></p></li><li><p><a href="?open=false#&#167;role-tests-characterization-tests-and-differential-tests">Role tests, characterization tests, and differential tests</a></p></li><li><p><a href="?open=false#&#167;what-should-go-into-a-role-test">What should go into a role test?</a></p></li><li><p><a href="?open=false#&#167;what-should-not-go-into-a-role-test">What should not go into a role test?</a></p></li><li><p><a href="?open=false#&#167;role-tests-and-object-oriented-design">Role tests and object-oriented design</a></p></li><li><p><a href="?open=false#&#167;common-mistakes">Common mistakes</a></p></li><li><p><a href="?open=false#&#167;when-role-tests-are-worth-the-cost">When role tests are worth the cost</a></p></li><li><p><a href="?open=false#&#167;conclusion">Conclusion</a></p></li><li><p><a href="?open=false#&#167;references">References</a></p></li></ul><h2>The problem with interfaces</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!41CT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea311c6-9ef7-4ad9-8464-5e0f578b4676_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!41CT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea311c6-9ef7-4ad9-8464-5e0f578b4676_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!41CT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea311c6-9ef7-4ad9-8464-5e0f578b4676_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!41CT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea311c6-9ef7-4ad9-8464-5e0f578b4676_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!41CT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea311c6-9ef7-4ad9-8464-5e0f578b4676_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!41CT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea311c6-9ef7-4ad9-8464-5e0f578b4676_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ea311c6-9ef7-4ad9-8464-5e0f578b4676_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:902270,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea311c6-9ef7-4ad9-8464-5e0f578b4676_1024x559.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_!41CT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea311c6-9ef7-4ad9-8464-5e0f578b4676_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!41CT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea311c6-9ef7-4ad9-8464-5e0f578b4676_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!41CT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea311c6-9ef7-4ad9-8464-5e0f578b4676_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!41CT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea311c6-9ef7-4ad9-8464-5e0f578b4676_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Interfaces are useful because they let us name a role without committing every client to one concrete implementation.</p><p>They appear around persistence, clocks, payment gateways, event publishers, file systems, external APIs, pricing policies, routing algorithms, feature flags, and other boundaries where substitution matters.</p><p>But an interface is incomplete.</p><p>It defines what can be called, not everything those calls are expected to mean.</p><p>Take the repository again:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;9535426b-0159-49e8-8ac1-e228eca122d6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerRepository {
    fun findById(id: CustomerId): CustomerLookup
    fun save(customer: Customer)
}</code></pre></div><p>The type signature answers some questions:</p><ul><li><p>Which messages can a client send?</p></li><li><p>Which arguments are required?</p></li><li><p>Which result type is returned?</p></li><li><p>Which implementations can be supplied where <code>CustomerRepository</code> is expected?</p></li></ul><p>It leaves other questions unanswered:</p><ul><li><p>What should be true after a customer is saved?</p></li><li><p>Which customer fields must be preserved?</p></li><li><p>What happens when no customer has the requested id?</p></li><li><p>Should customers with different ids remain independent?</p></li><li><p>What happens when the same id is saved twice?</p></li><li><p>Which failures can clients observe?</p></li><li><p>Is saved data immediately visible to a later lookup?</p></li><li><p>Does the role promise durable storage, or only retrieval during the life of the object?</p></li><li><p>Is the implementation expected to be safe under concurrent access?</p></li></ul><p>A repository contract does not need to answer every possible question. It must state the behaviours clients are allowed to rely on instead of relying on a familiar method name.</p><p>The word <code>save</code> feels meaningful because developers bring conventions to it. One reads it as insert. Another reads it as upsert. Another expects optimistic concurrency. Another assumes the call returns only after durable persistence.</p><p>The compiler cannot resolve those interpretations. It verifies structural compatibility, not the full meaning of the abstraction.</p><p>A behavioural contract can include:</p><ul><li><p>Preconditions: what must be true before a call</p></li><li><p>Postconditions: what the implementation guarantees after a call</p></li><li><p>Invariants: what remains true across operations</p></li><li><p>Failure semantics: which failures clients may observe and how they are represented</p></li><li><p>History properties: how sequences of calls affect later observations</p></li></ul><p>Barbara Liskov and Jeannette Wing&#8217;s work on behavioural subtyping makes the underlying point precise: syntactic compatibility is not sufficient. An implementation must preserve the properties that clients are entitled to assume from the abstraction&#8217;s specification.</p><p>A repository contract might contain this property:</p><pre><code><code>Given a valid customer,
when the client saves it,
then a later lookup by the same id returns that customer.</code></code></pre><p>It might also contain:</p><pre><code><code>Given no saved customer with an id,
when the client looks up that id,
then the repository reports that the customer is missing.</code></code></pre><p>If clients rely on replacement semantics, it might additionally contain:</p><pre><code><code>Given a previously saved customer,
when the client saves another customer with the same id,
then a later lookup returns the most recently saved customer.</code></code></pre><p>These statements describe relationships between calls. The interface alone cannot express them.</p><p>The first shift is:</p><blockquote><p>An interface is not the whole contract. It is the typed surface through which the contract is exercised.</p></blockquote><p>The role test gives part of that contract an executable form.</p><h2>What a role test is</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZuSY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59551594-d44b-4995-9a5d-097c98a04719_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZuSY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59551594-d44b-4995-9a5d-097c98a04719_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ZuSY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59551594-d44b-4995-9a5d-097c98a04719_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ZuSY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59551594-d44b-4995-9a5d-097c98a04719_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ZuSY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59551594-d44b-4995-9a5d-097c98a04719_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZuSY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59551594-d44b-4995-9a5d-097c98a04719_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/59551594-d44b-4995-9a5d-097c98a04719_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:581423,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59551594-d44b-4995-9a5d-097c98a04719_1024x559.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_!ZuSY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59551594-d44b-4995-9a5d-097c98a04719_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ZuSY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59551594-d44b-4995-9a5d-097c98a04719_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ZuSY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59551594-d44b-4995-9a5d-097c98a04719_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ZuSY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59551594-d44b-4995-9a5d-097c98a04719_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A role test is a reusable suite of behavioural examples that states what every valid implementation of a role must make true for its clients.</p><p>In responsibility-driven design, a role is a coherent set of related responsibilities within a collaboration. An object can play one or more roles. A responsibility is an obligation to perform work, make a decision, or know information. A collaboration is an interaction between objects or roles.</p><p><code>CustomerRepository</code> can be understood as a role with responsibilities such as:</p><ul><li><p>Remember a customer presented by a client</p></li><li><p>Find a remembered customer by identity</p></li><li><p>Represent absence using the agreed domain language</p></li></ul><p>The role can be played by several objects:</p><ul><li><p><code>InMemoryCustomerRepository</code></p></li><li><p><code>PostgresCustomerRepository</code></p></li><li><p><code>FileCustomerRepository</code></p></li><li><p><code>RemoteCustomerRepository</code></p></li></ul><p>The role is not any one of those classes. It is the set of responsibilities they claim to fulfil for their clients.</p><p>A role test says:</p><blockquote><p>Any implementation that claims to be a <code>CustomerRepository</code> should satisfy these examples.</p></blockquote><p>Conceptually:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QCfv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c082ef3-88c1-4cca-9511-b911864c7262_1454x1068.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QCfv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c082ef3-88c1-4cca-9511-b911864c7262_1454x1068.png 424w, https://substackcdn.com/image/fetch/$s_!QCfv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c082ef3-88c1-4cca-9511-b911864c7262_1454x1068.png 848w, https://substackcdn.com/image/fetch/$s_!QCfv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c082ef3-88c1-4cca-9511-b911864c7262_1454x1068.png 1272w, https://substackcdn.com/image/fetch/$s_!QCfv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c082ef3-88c1-4cca-9511-b911864c7262_1454x1068.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QCfv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c082ef3-88c1-4cca-9511-b911864c7262_1454x1068.png" width="1454" height="1068" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4c082ef3-88c1-4cca-9511-b911864c7262_1454x1068.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1068,&quot;width&quot;:1454,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:82118,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c082ef3-88c1-4cca-9511-b911864c7262_1454x1068.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_!QCfv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c082ef3-88c1-4cca-9511-b911864c7262_1454x1068.png 424w, https://substackcdn.com/image/fetch/$s_!QCfv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c082ef3-88c1-4cca-9511-b911864c7262_1454x1068.png 848w, https://substackcdn.com/image/fetch/$s_!QCfv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c082ef3-88c1-4cca-9511-b911864c7262_1454x1068.png 1272w, https://substackcdn.com/image/fetch/$s_!QCfv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c082ef3-88c1-4cca-9511-b911864c7262_1454x1068.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><a href="https://mermaid.live/edit#pako:eNptkbFuwzAMRH9F4BwPSTYPXZIMAdqitTu17sBYtC1AFg2aShEE-ffKToM2QLej7vFwEM9QsyXIofH8VXcoat62VTDm-aOCjXcU1AQiO1bwabLswRTpvWBPpuaggrUmY-KL2S2TW6YUsuZAHR4dR0FvxuiUfsByBvfLRO5D1lPPcjJocVCSe2SVkBcetRUqXx__Z9ZTHRo81tRPXe-h_XKmdgnaHZ2lUJPhJtU5jOo0Kh6cd3oyDYtRGvVv7VvE6hox6_VVwwJacRZylUgL6El6nEY4T1QF2qUyFeRJWmow-vRJVbiktQHDO3N_2xSObQd5g35MUxwsKm0dtoK_CAVLsuEYFPLlAsg6ZXm6Hm2-3eUb_vaTQA">Open and edit this diagram in Mermaid Live</a></figcaption></figure></div><p>The role test normally contains:</p><ol><li><p>Shared examples that describe the role</p></li><li><p>A factory or fixture hook supplied by each concrete implementation test</p></li></ol><p>This makes the suite executable documentation. A future reader can inspect it to understand what the abstraction means in this codebase, not only which methods it exposes.</p><p>That matters because weak abstractions rarely announce themselves with invalid method signatures. They appear when:</p><ul><li><p>Different implementations interpret the same method differently</p></li><li><p>A fake becomes more permissive than production</p></li><li><p>A replacement dependency preserves API shape but changes behaviour</p></li><li><p>Clients begin to rely on behaviour that was never made explicit</p></li><li><p>A shared interface is hiding several incompatible roles</p></li></ul><p>A role test gives those behaviours a home.</p><h2>Are role tests contract tests?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uSrq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd6678f-4fe7-419b-a5b5-0c83a8e48feb_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uSrq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd6678f-4fe7-419b-a5b5-0c83a8e48feb_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uSrq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd6678f-4fe7-419b-a5b5-0c83a8e48feb_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uSrq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd6678f-4fe7-419b-a5b5-0c83a8e48feb_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uSrq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd6678f-4fe7-419b-a5b5-0c83a8e48feb_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uSrq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd6678f-4fe7-419b-a5b5-0c83a8e48feb_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7cd6678f-4fe7-419b-a5b5-0c83a8e48feb_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:977597,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd6678f-4fe7-419b-a5b5-0c83a8e48feb_1024x559.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_!uSrq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd6678f-4fe7-419b-a5b5-0c83a8e48feb_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uSrq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd6678f-4fe7-419b-a5b5-0c83a8e48feb_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uSrq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd6678f-4fe7-419b-a5b5-0c83a8e48feb_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uSrq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd6678f-4fe7-419b-a5b5-0c83a8e48feb_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In the broad object-oriented sense, a role test is a form of contract test.</p><p>The older contract idea comes from Bertrand Meyer&#8217;s <em>Design by Contract</em> (DbC). In that model, a call is an agreement between a client and a supplier. The client must satisfy the preconditions for using the operation. The supplier must guarantee the promised postconditions and preserve the relevant invariants.</p><p>Role tests do not require a DbC language or runtime assertion mechanism. They borrow the same discipline: make the client-supplier promise explicit enough that it can be checked.</p><p>For a repository role, the agreement might say:</p><ul><li><p>Precondition: the customer has a valid identity.</p></li><li><p>Postcondition: a later lookup by that identity returns the saved customer.</p></li><li><p>Invariant: different customer identities remain independent.</p></li></ul><p>The role test turns those promises into examples that every implementation must satisfy.</p><p>Manuel Rivero calls them &#8220;role or contract tests.&#8221; His definition starts from the contract clients establish with a role and then tests every adapter to check that it respects that contract. He prefers the name <em>role test</em> because <em>contract test</em> has become overloaded.</p><p>Jason Gorman describes the same reusable-suite pattern as &#8220;polymorphic/contract testing&#8221;: test methods live in a base suite, while concrete test subclasses provide different implementations. Rivero also relates role tests to the contract tests in J. B. Rainsberger&#8217;s collaboration-and-contract testing model.</p><p>That vocabulary connects several older ideas. <em>Mock Roles, not Objects</em> explains how client tests discover outgoing interfaces from the services a collaboration needs. Liskov and Wing&#8217;s behavioural subtyping work explains why substitutability requires preserving specified properties, not merely matching method signatures. Fowler&#8217;s writing on contract tests uses the same broad contract language for a different risk: compatibility between independently changing services. The overlap is useful, but the testing target is different.</p><p>The resulting definition is:</p><blockquote><p>A role test is an object-oriented contract test expressed as a reusable behavioural suite and applied to every implementation that claims the same role.</p></blockquote><p>The distinctive move is to execute one contract against two or more alternatives that must be substitutable.</p><p>The word <em>interfaces</em> can mislead here.</p><p>The sources describe testing different implementations or adapters of a role, not testing a contract against several unrelated interfaces. Usually there is one role interface:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;de6f3d48-76e5-4173-80ff-4bed8a68ad71&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">CustomerRepository</code></pre></div><p>and several implementations:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;f1d2ce70-7dae-4f58-85e4-f285936dca8e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">InMemoryCustomerRepository
PostgresCustomerRepository
ServiceCustomerRepository</code></pre></div><p>In a dynamic language, there may be no formal interface construct at all. The role can be a duck type documented by shared examples. This is one reason &#8220;two or more interfaces&#8221; cannot be the general definition.</p><p>When two external dependencies expose different interfaces, adapters translate both into one stable role interface. The role suite runs against the adapters:</p><pre><code><code>Legacy API -&gt; Legacy adapter -&gt; CustomerRepository contract
New API    -&gt; New adapter    -&gt; CustomerRepository contract</code></code></pre><p>A second implementation is not logically required to write the suite. The suite can document a role before another implementation exists. Its substitutability value becomes visible when a fake, replacement, alternative strategy, or second adapter is added.</p><p>The terminology becomes confusing because <em>contract testing</em> also has a narrower and now widespread meaning in distributed systems. Martin Fowler uses the term for tests that check whether an external service continues to satisfy the behaviour expected by a client. Consumer-driven contract-testing literature develops that idea further around consumers, providers, published interactions, and independent deployment.</p><p>The two usages protect different risks:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GEjK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc36d97-3a09-42a8-ac4f-1c12f66781d5_1303x1518.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GEjK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc36d97-3a09-42a8-ac4f-1c12f66781d5_1303x1518.png 424w, https://substackcdn.com/image/fetch/$s_!GEjK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc36d97-3a09-42a8-ac4f-1c12f66781d5_1303x1518.png 848w, https://substackcdn.com/image/fetch/$s_!GEjK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc36d97-3a09-42a8-ac4f-1c12f66781d5_1303x1518.png 1272w, https://substackcdn.com/image/fetch/$s_!GEjK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc36d97-3a09-42a8-ac4f-1c12f66781d5_1303x1518.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GEjK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc36d97-3a09-42a8-ac4f-1c12f66781d5_1303x1518.png" width="1303" height="1518" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7fc36d97-3a09-42a8-ac4f-1c12f66781d5_1303x1518.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1518,&quot;width&quot;:1303,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:104580,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc36d97-3a09-42a8-ac4f-1c12f66781d5_1303x1518.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_!GEjK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc36d97-3a09-42a8-ac4f-1c12f66781d5_1303x1518.png 424w, https://substackcdn.com/image/fetch/$s_!GEjK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc36d97-3a09-42a8-ac4f-1c12f66781d5_1303x1518.png 848w, https://substackcdn.com/image/fetch/$s_!GEjK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc36d97-3a09-42a8-ac4f-1c12f66781d5_1303x1518.png 1272w, https://substackcdn.com/image/fetch/$s_!GEjK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc36d97-3a09-42a8-ac4f-1c12f66781d5_1303x1518.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><a href="https://mermaid.live/edit#pako:eNp9UsFOwzAM_RUr55WJHXtAGttlErCp7ATlkDZuG2jjKnVA07R_x-0o3YTEKXH83vOz46PKyaCKVVHTV15pz_CQpA6gC1npdVvBdvuaqm32jjlH5C06RgM5OfY6Z2Ds2LoyVW89CSB5FnRCNcJ8As2hpfrQkG8rmw8UkbeME2szsqzI-0LnCOTBhPwD-NBOwOWtADdNW2MjRjRbcrCcsou_2fsLaxBFdyJxHS5GCxDdXKbHeMijM6m7Gsu6b3RtO_Y2C_-OZLV9EuiKXBca9JPZZC_Pu5DVtquEP3Qu_N70qPUL3iX9L-w8fVpzoSHS5yaS_XAK7MetmqnSW6Ni9gFnSgo3ug_VsQekiiuZUapiuRosdKilVupOQmu1eyFqRqanUFYqLnTdSRRaoxnXVssQJojUQ7-i4FjFtzOFxjL5x_NeDet1-gZK1MZQ">Open and edit this diagram in Mermaid Live</a></figcaption></figure></div><p>Object-oriented role or contract test Distributed contract test Protects substitutability between implementations Protects compatibility between independently changing participants Runs one behavioural suite against every implementation Verifies requests, responses, messages, schemas, and interaction semantics Common examples are fake versus real repository or old versus new strategy Common examples are mobile client versus HTTP API or event consumer versus producer The contract is usually owned inside one design The contract crosses a deployment, team, or service boundary</p><p>An HTTP-backed repository may need both. Its role suite checks that the adapter presents the expected <code>CustomerRepository</code> semantics to local clients. Its distributed contract tests check that the remote provider still accepts and returns the agreed protocol.</p><p>Here, <em>role test</em> names the object-oriented, polymorphic use: one behavioural suite, one role, every implementation that claims it. Distributed contract testing remains a separate compatibility concern at service or deployment boundaries.</p><h2>What a role test is not</h2><h3>It is not a direct implementation comparison</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_nSw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae651f5-4e89-4d25-b64e-97e3c0c9a3cb_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_nSw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae651f5-4e89-4d25-b64e-97e3c0c9a3cb_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!_nSw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae651f5-4e89-4d25-b64e-97e3c0c9a3cb_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!_nSw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae651f5-4e89-4d25-b64e-97e3c0c9a3cb_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!_nSw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae651f5-4e89-4d25-b64e-97e3c0c9a3cb_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_nSw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae651f5-4e89-4d25-b64e-97e3c0c9a3cb_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4ae651f5-4e89-4d25-b64e-97e3c0c9a3cb_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:848397,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae651f5-4e89-4d25-b64e-97e3c0c9a3cb_1024x559.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_!_nSw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae651f5-4e89-4d25-b64e-97e3c0c9a3cb_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!_nSw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae651f5-4e89-4d25-b64e-97e3c0c9a3cb_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!_nSw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae651f5-4e89-4d25-b64e-97e3c0c9a3cb_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!_nSw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ae651f5-4e89-4d25-b64e-97e3c0c9a3cb_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It is tempting to describe role testing as &#8220;testing two implementations of the same interface against each other.&#8221;</p><p>That wording is understandable but imprecise.</p><p>If we frame the technique that way, one implementation can accidentally become the reference and the other becomes a copy.</p><p>The flow should not be:</p><pre><code><code>PostgreSQL behaviour -&gt; copy into the in-memory fake</code></code></pre><p>It should be:</p><pre><code><code>client needs -&gt; role contract -&gt; shared examples -&gt; implementations</code></code></pre><p>The wrong oracle is:</p><pre><code><code>InMemoryCustomerRepository behaves like PostgresCustomerRepository.</code></code></pre><p>The role oracle is:</p><pre><code><code>InMemoryCustomerRepository satisfies CustomerRepositoryRoleShould.
PostgresCustomerRepository satisfies CustomerRepositoryRoleShould.</code></code></pre><p>The role is the reference, not one implementation.</p><p>If the implementations disagree, the answer is not automatically &#8220;make the fake match PostgreSQL&#8221; or &#8220;make PostgreSQL match the fake.&#8221;</p><p>Possible explanations include:</p><ul><li><p>The fake is wrong</p></li><li><p>The production adapter is wrong</p></li><li><p>The old implementation contains accidental behaviour</p></li><li><p>The role test is missing a client-visible rule</p></li><li><p>The abstraction promises something one implementation cannot provide</p></li><li><p>Two different roles are hiding behind one interface</p></li></ul><h3>It is not differential testing</h3><p>Differential testing runs the same inputs through two implementations and compares their outputs.</p><p>That can be valuable when replacing a legacy algorithm, database query, parser, or library. It can reveal disagreements over a large input set.</p><p>But it uses one implementation, or agreement between implementations, as an oracle. If the old implementation contains a bug, differential testing can encourage the new implementation to reproduce it.</p><p>A role test uses an intentional behavioural specification as the oracle.</p><h3>It is not only a way to remove duplicated test code</h3><p>Test reuse is useful. If several implementations must satisfy the same examples, copying those tests would create maintenance risk.</p><p>The deeper value is semantic:</p><ul><li><p>The team states the shared behaviour once</p></li><li><p>Every implementation is held to the same meaning</p></li><li><p>A disagreement becomes a design decision</p></li><li><p>A new implementation tests the abstraction itself</p></li></ul><h3>It is not a complete proof of substitutability</h3><p>A finite suite protects the examples and properties it encodes.</p><p>It does not automatically establish:</p><ul><li><p>Every possible input</p></li><li><p>Every operation sequence</p></li><li><p>Thread safety</p></li><li><p>Durability</p></li><li><p>Performance</p></li><li><p>Security</p></li><li><p>Availability</p></li><li><p>Untested failures</p></li></ul><p>Passing the suite is evidence that an implementation satisfies the represented portion of the contract. It is not a formal proof that every client-visible property is preserved.</p><h3>It is not a replacement for implementation-specific tests</h3><p>A PostgreSQL repository still needs tests for migrations, constraints, transactions, mapping, and database integration. A filesystem adapter may need tests for paths, permissions, atomic writes, and serialization. An HTTP adapter may need tests for authentication, timeouts, status-code translation, and retry behaviour.</p><p>Those tests protect implementation-specific responsibilities.</p><p>The role test protects shared behaviour.</p><h3>It is not a particular test inheritance mechanism</h3><p>A reusable role suite can use:</p><ul><li><p>A Kotlin or Java test interface</p></li><li><p>An abstract test class</p></li><li><p>Parameterized tests</p></li><li><p>Dynamic tests</p></li><li><p>RSpec shared examples</p></li><li><p>Composition through functions or fixtures</p></li></ul><p>The mechanism is secondary. The reusable behavioural specification is the technique.</p><h2>Roles are discovered from the client side</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CDu_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F274d53bb-935e-491c-9d96-27a2df6a8dc9_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CDu_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F274d53bb-935e-491c-9d96-27a2df6a8dc9_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!CDu_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F274d53bb-935e-491c-9d96-27a2df6a8dc9_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!CDu_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F274d53bb-935e-491c-9d96-27a2df6a8dc9_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!CDu_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F274d53bb-935e-491c-9d96-27a2df6a8dc9_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CDu_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F274d53bb-935e-491c-9d96-27a2df6a8dc9_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/274d53bb-935e-491c-9d96-27a2df6a8dc9_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:659320,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F274d53bb-935e-491c-9d96-27a2df6a8dc9_1024x559.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_!CDu_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F274d53bb-935e-491c-9d96-27a2df6a8dc9_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!CDu_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F274d53bb-935e-491c-9d96-27a2df6a8dc9_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!CDu_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F274d53bb-935e-491c-9d96-27a2df6a8dc9_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!CDu_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F274d53bb-935e-491c-9d96-27a2df6a8dc9_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A role does not usually appear because we invented an interface in isolation.</p><p>It often appears because a client needs help.</p><p>A use case needs somewhere to save a customer. A workflow needs a way to publish an event. A policy needs a source of time. A checkout process needs a pricing strategy. A migration needs an abstraction around a library that is being replaced.</p><p>In each case, the client is not asking for a concrete class. It is asking for a collaborator that can play a useful role in a larger behaviour.</p><p>Imagine we are developing <code>RegisterCustomer</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;e8ebe7e5-2096-4421-97af-403bc78b5127&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class RegisterCustomer(
    private val customers: CustomerRepository
) {
    fun register(command: RegisterCustomerCommand) {
        val customer = Customer(
            id = command.customerId,
            name = command.name
        )

        customers.save(customer)
    }
}</code></pre></div><p>At first, the test does not need a database. It needs to express that successful registration asks something to remember the customer.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;3c2e3780-25f7-459b-977d-45862b79abb2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">@Test
fun `register a customer`() {
    val customers = RecordingCustomerRepository()
    val useCase = RegisterCustomer(customers)

    useCase.register(
        RegisterCustomerCommand(
            customerId = CustomerId("customer-1"),
            name = "Alice"
        )
    )

    assertThat(customers.savedCustomers).containsExactly(
        Customer(
            id = CustomerId("customer-1"),
            name = "Alice"
        )
    )
}</code></pre></div><p>The recording repository is a client-test double. It records the outgoing collaboration that this client needs and refuses unrelated messages:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;d86c70da-88d1-471b-9f51-0e6f7df7a566&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class RecordingCustomerRepository : CustomerRepository {
    val savedCustomers = mutableListOf&lt;Customer&gt;()

    override fun save(customer: Customer) {
        savedCustomers += customer
    }

    override fun findById(id: CustomerId): CustomerLookup {
        error("RegisterCustomer does not need lookup.")
    }
}</code></pre></div><p>The messages sent by the client begin to reveal a required role.</p><p>Later, another client needs lookup. Another needs replacement semantics. Another needs to distinguish missing data from infrastructure failure. The role becomes more precise as real client needs appear.</p><p>The discovery path moves from a client&#8217;s behaviour to a role and then to the implementations that must satisfy it:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!u3X8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b07867-c294-4ece-a113-d9142b2d90be_3013x436.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u3X8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b07867-c294-4ece-a113-d9142b2d90be_3013x436.png 424w, https://substackcdn.com/image/fetch/$s_!u3X8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b07867-c294-4ece-a113-d9142b2d90be_3013x436.png 848w, https://substackcdn.com/image/fetch/$s_!u3X8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b07867-c294-4ece-a113-d9142b2d90be_3013x436.png 1272w, https://substackcdn.com/image/fetch/$s_!u3X8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b07867-c294-4ece-a113-d9142b2d90be_3013x436.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!u3X8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b07867-c294-4ece-a113-d9142b2d90be_3013x436.png" width="1456" height="211" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a3b07867-c294-4ece-a113-d9142b2d90be_3013x436.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:211,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:72389,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b07867-c294-4ece-a113-d9142b2d90be_3013x436.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_!u3X8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b07867-c294-4ece-a113-d9142b2d90be_3013x436.png 424w, https://substackcdn.com/image/fetch/$s_!u3X8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b07867-c294-4ece-a113-d9142b2d90be_3013x436.png 848w, https://substackcdn.com/image/fetch/$s_!u3X8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b07867-c294-4ece-a113-d9142b2d90be_3013x436.png 1272w, https://substackcdn.com/image/fetch/$s_!u3X8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b07867-c294-4ece-a113-d9142b2d90be_3013x436.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption"><a href="https://mermaid.live/edit#pako:eNpVkcFOwzAMQH_F8nk7cO0BCXVHymHbCcrBbdw2UhoPJ2FC0_4dt2OauMXJe7ZjX7AXx1jhEOTcT6QZXvdtBDh-tFgHzzFD5pRb_ITt9hnqx3XHE317KWpvi1GvQGNAwynRyAmUv4pXdjCozEDQSwjUiVKWu9Ws1t6sN1KVM6gEBh8z60A9_1H7ldoZdbRmwEnpjCrJUnc_0D_6TP-El6Vbib1yZiBHJ8v6nzgYcbBvW6K1cCo-34sebjlwg6N6h1XWwhucWWdaQrwsUIt54tmUyo6OByrBhtXGq2kniu8i891UKeOE1UAhWVROjjLvPI1KD4SjY62lxIzV0wbZeRtVc1vRuqnrL6hZjro">Open and edit this diagram in Mermaid Live</a></figcaption></figure></div><p>Role tests fit naturally with <em>Test-Driven Development</em> (TDD) and need-driven design.</p><p>The paper <em>Mock Roles, not Objects</em> explains how tests for a client can discover outgoing interfaces based on services the client requires. The interface is pulled into existence by demand rather than pushed outward from all the features a provider happens to offer.</p><p>Two testing activities follow:</p><ol><li><p>Client tests protect how the client collaborates with the role</p></li><li><p>Role tests protect whether concrete implementations honour that role</p></li></ol><p>The client test does not prove that PostgreSQL works. The PostgreSQL role test does not prove that <code>RegisterCustomer</code> sends the correct message. Together they protect both sides of the collaboration.</p><p>Client needs change how interfaces are designed.</p><p>A good role interface is not a list of everything an implementation can do. It is a description of what a client needs from a collaborator.</p><p>Consider a provider-oriented interface:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;edebc03c-32b0-4fa7-a6eb-a60bc89ed9de&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerStore {
    fun save(customer: Customer)
    fun findById(id: CustomerId): CustomerLookup
    fun deleteAll()
    fun rebuildIndex()
    fun exportCsv(): String
    fun compactStorage()
}</code></pre></div><p>Those operations may exist in one infrastructure component, but they do not form one coherent client role.</p><p>Application clients may need:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;11851e28-7b41-4963-b5a2-40007728f3e1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerRepository {
    fun save(customer: Customer)
    fun findById(id: CustomerId): CustomerLookup
}</code></pre></div><p>Operational tooling may need:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;a67b6a9f-4c6a-4f24-ba97-9ebc21ac56f3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerStorageMaintenance {
    fun rebuildIndex()
    fun compactStorage()
}</code></pre></div><p>Narrow roles are easier to understand, substitute, adapt, and role-test.</p><h2>A small example in Kotlin</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NdNZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe034c9e8-bdc2-42c2-8c38-a97cb0bbfae2_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NdNZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe034c9e8-bdc2-42c2-8c38-a97cb0bbfae2_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!NdNZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe034c9e8-bdc2-42c2-8c38-a97cb0bbfae2_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!NdNZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe034c9e8-bdc2-42c2-8c38-a97cb0bbfae2_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!NdNZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe034c9e8-bdc2-42c2-8c38-a97cb0bbfae2_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NdNZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe034c9e8-bdc2-42c2-8c38-a97cb0bbfae2_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e034c9e8-bdc2-42c2-8c38-a97cb0bbfae2_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:524375,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe034c9e8-bdc2-42c2-8c38-a97cb0bbfae2_1024x559.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_!NdNZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe034c9e8-bdc2-42c2-8c38-a97cb0bbfae2_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!NdNZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe034c9e8-bdc2-42c2-8c38-a97cb0bbfae2_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!NdNZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe034c9e8-bdc2-42c2-8c38-a97cb0bbfae2_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!NdNZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe034c9e8-bdc2-42c2-8c38-a97cb0bbfae2_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The repository example is familiar, but role tests are not mainly about repositories.</p><p>Role tests also apply to clocks, payment gateways, event publishers, file adapters, HTTP clients, caches, notification senders, search adapters, and Strategy implementations.</p><p>Start with a small domain:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;106852ec-d5f0-4717-94b8-7db15d19ed56&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class CustomerId(val value: String) {
    init {
        require(value.isNotBlank()) {
            "Customer id must not be blank."
        }
    }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;44984a81-12fa-425b-99e8-1c71363a1857&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class Customer(
    val id: CustomerId,
    val name: String
) {
    init {
        require(name.isNotBlank()) {
            "Customer name must not be blank."
        }
    }
}</code></pre></div><p>Now decide how lookup absence appears in the role&#8217;s language.</p><p>A nullable result would be concise:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;5b499c07-9b54-4f84-b11c-6bf88a8a9443&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">fun findById(id: CustomerId): Customer?</code></pre></div><p>An explicit result makes the contract easier to see:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;d57f0c98-7031-4397-9c2a-a4447d7ef178&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">sealed interface CustomerLookup {
    data class Found(val customer: Customer) : CustomerLookup
    data class Missing(val requestedId: CustomerId) : CustomerLookup
}</code></pre></div><p>The repository becomes:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;92b6997d-6d77-4c89-8b34-70ae8ee03c93&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerRepository {
    fun findById(id: CustomerId): CustomerLookup
    fun save(customer: Customer)
}</code></pre></div><p>The result type already communicates one decision: absence is a normal lookup outcome represented as <code>Missing</code>, not <code>null</code> or an exception.</p><p>It still does not define the relationship between calls. The role suite will do that.</p><p>JUnit Jupiter allows test methods to be declared in interfaces. A concrete test class can implement the interface, supply a factory method, and inherit the shared examples.</p><p>The snippets below are complete for the design point they teach. Database, service, or sandbox harnesses are shown only where their shape affects the role-test contract.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;f0077db3-53ed-4047-a1f6-430ff566a3a2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

interface CustomerRepositoryRoleShould {

    fun createEmptyRepository(): CustomerRepository

    @Test
    fun `find saved customer by id`() {
        // Arrange
        val repository = createEmptyRepository()
        val customer = Customer(
            id = CustomerId("customer-1"),
            name = "Alice"
        )

        // Act
        repository.save(customer)
        val result = repository.findById(customer.id)

        // Assert
        assertThat(result)
            .isEqualTo(CustomerLookup.Found(customer))
    }

    @Test
    fun `return missing when customer does not exist`() {
        // Arrange
        val repository = createEmptyRepository()
        val unknownId = CustomerId("unknown")

        // Act
        val result = repository.findById(unknownId)

        // Assert
        assertThat(result)
            .isEqualTo(CustomerLookup.Missing(unknownId))
    }

    @Test
    fun `keep saved customers separated by id`() {
        // Arrange
        val repository = createEmptyRepository()
        val alice = Customer(
            id = CustomerId("customer-1"),
            name = "Alice"
        )
        val bob = Customer(
            id = CustomerId("customer-2"),
            name = "Bob"
        )

        // Act
        repository.save(alice)
        repository.save(bob)

        val foundAlice = repository.findById(alice.id)
        val foundBob = repository.findById(bob.id)

        // Assert
        assertThat(foundAlice)
            .isEqualTo(CustomerLookup.Found(alice))
        assertThat(foundBob)
            .isEqualTo(CustomerLookup.Found(bob))
    }

    @Test
    fun `preserve relevant customer data`() {
        // Arrange
        val repository = createEmptyRepository()
        val customer = Customer(
            id = CustomerId("customer-1"),
            name = "Zo&#235; &#26446;"
        )

        // Act
        repository.save(customer)
        val result = repository.findById(customer.id)

        // Assert
        assertThat(result)
            .isEqualTo(CustomerLookup.Found(customer))
    }
}</code></pre></div><p>The tests use Arrange, Act, Assert because it makes each expectation explicit. This structure is not part of role testing itself. It is simply a readable way to express each behavioural example.</p><p>The suite observes the repository only through its public role. It says nothing about maps, SQL, transactions, files, HTTP requests, JSON, or connection pools.</p><p>That is intentional.</p><p>The factory name also states a fixture precondition:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;3f63bc2d-f953-49f8-bd81-7ffe0e57ffdb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">fun createEmptyRepository(): CustomerRepository</code></pre></div><p>Every implementation-specific test must provide a clean repository. Without isolation, shared examples can leak state into one another and stop being reliable specifications.</p><p>The naming convention is deliberate. <code>CustomerRepositoryRoleShould</code> makes the role the subject of the specification:</p><pre><code><code>CustomerRepositoryRoleShould find saved customer by id.
CustomerRepositoryRoleShould return missing when customer does not exist.
CustomerRepositoryRoleShould keep saved customers separated by id.</code></code></pre><p>The individual method names remain short while the complete test name communicates the behavioural subject.</p><h2>The first testing smell: using one role method to arrange another</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mqvP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa437fb22-f5b1-4397-bd58-3f40a17d0136_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mqvP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa437fb22-f5b1-4397-bd58-3f40a17d0136_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!mqvP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa437fb22-f5b1-4397-bd58-3f40a17d0136_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!mqvP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa437fb22-f5b1-4397-bd58-3f40a17d0136_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!mqvP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa437fb22-f5b1-4397-bd58-3f40a17d0136_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mqvP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa437fb22-f5b1-4397-bd58-3f40a17d0136_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a437fb22-f5b1-4397-bd58-3f40a17d0136_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:763795,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa437fb22-f5b1-4397-bd58-3f40a17d0136_1024x559.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_!mqvP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa437fb22-f5b1-4397-bd58-3f40a17d0136_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!mqvP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa437fb22-f5b1-4397-bd58-3f40a17d0136_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!mqvP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa437fb22-f5b1-4397-bd58-3f40a17d0136_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!mqvP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa437fb22-f5b1-4397-bd58-3f40a17d0136_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The first suite is good enough to introduce the idea, but it also hides a testing smell.</p><p>The example named <code>find saved customer by id</code> uses <code>save</code> to create the precondition and <code>findById</code> to make the observation. If it fails, the failure might be in saving, lookup, mapping, equality, fixture isolation, or the relationship between two calls.</p><p>Some role examples genuinely need a sequence of role messages. If the contract is about a conversation, the test should show the conversation. The problem is accidental sequencing caused by weak fixture support.</p><p>A useful rule is:</p><blockquote><p>If a test is about lookup, do not use <code>save</code> to create the precondition. If a test is about saving, do not use <code>findById</code> to observe the result.</p></blockquote><p>The test should fail for the behaviour it is trying to specify.</p><p>For lookup behaviour, the repository should already contain the customer. For saving behaviour, the test should observe stored state without going back through lookup.</p><p>That does not mean exposing implementation details to production code. It means giving the test suite implementation-specific fixture hooks that can prepare and inspect state outside the role method currently under test.</p><h2>A precise role-test fixture</h2><p>A sharper role suite separates the production role from test infrastructure:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;8116df4c-0f3d-4d6c-880d-13ccaadf8ca3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerRepositoryFixture {
    val repository: CustomerRepository

    fun givenStored(vararg customers: Customer)

    fun storedCustomerById(id: CustomerId): CustomerLookup
}</code></pre></div><p>Now the shared examples can say exactly which behaviour they exercise:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;e5b25876-54bc-4064-ba1a-d33f51729c7a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerRepositoryRoleShould {

    fun createFixture(): CustomerRepositoryFixture

    @Test
    fun `find stored customer by id`() {
        // Arrange
        val fixture = createFixture()
        val customer = Customer(
            id = CustomerId("customer-1"),
            name = "Alice"
        )

        fixture.givenStored(customer)

        // Act
        val result = fixture.repository.findById(customer.id)

        // Assert
        assertThat(result)
            .isEqualTo(CustomerLookup.Found(customer))
    }

    @Test
    fun `store a new customer`() {
        // Arrange
        val fixture = createFixture()
        val customer = Customer(
            id = CustomerId("customer-1"),
            name = "Alice"
        )

        // Act
        fixture.repository.save(customer)

        // Assert
        assertThat(fixture.storedCustomerById(customer.id))
            .isEqualTo(CustomerLookup.Found(customer))
    }

    @Test
    fun `return missing when customer does not exist`() {
        // Arrange
        val fixture = createFixture()
        val unknownId = CustomerId("unknown")

        // Act
        val result = fixture.repository.findById(unknownId)

        // Assert
        assertThat(result)
            .isEqualTo(CustomerLookup.Missing(unknownId))
    }

    @Test
    fun `keep stored customers separated by id`() {
        // Arrange
        val fixture = createFixture()
        val alice = Customer(
            id = CustomerId("customer-1"),
            name = "Alice"
        )
        val bob = Customer(
            id = CustomerId("customer-2"),
            name = "Bob"
        )

        fixture.givenStored(alice, bob)

        // Act
        val foundAlice = fixture.repository.findById(alice.id)
        val foundBob = fixture.repository.findById(bob.id)

        // Assert
        assertThat(foundAlice)
            .isEqualTo(CustomerLookup.Found(alice))
        assertThat(foundBob)
            .isEqualTo(CustomerLookup.Found(bob))
    }

    @Test
    fun `preserve relevant customer data`() {
        // Arrange
        val fixture = createFixture()
        val customer = Customer(
            id = CustomerId("customer-1"),
            name = "Zo&#235; &#26446;"
        )

        // Act
        fixture.repository.save(customer)

        // Assert
        assertThat(fixture.storedCustomerById(customer.id))
            .isEqualTo(CustomerLookup.Found(customer))
    }
}</code></pre></div><p>The lookup example arranges state through <code>givenStored</code> and exercises only <code>findById</code>. The save example exercises only <code>save</code> and observes state through <code>storedCustomerById</code>.</p><p>The fixture is not part of the production role. The in-memory fixture can write directly to the backing map. The PostgreSQL fixture can prepare and inspect rows through the table gateway. The shared suite stays focused on client-visible behaviour, while each concrete fixture owns the mechanics needed to make the examples precise.</p><p>That precision matters because role tests often become design evidence. A noisy test that can fail for five reasons is a weak witness. A role test should make the missing or broken contract visible.</p><p>The change in test shape is small but important.</p><p>Before, a lookup example arranged state through the role method <code>save</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;08cad27f-3916-4093-b042-bd09b11c70ce&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">val repository = createEmptyRepository()

repository.save(customer)

assertThat(repository.findById(customer.id))
    .isEqualTo(CustomerLookup.Found(customer))</code></pre></div><p>After, the same lookup example uses fixture infrastructure for the precondition and reserves the role call for the behaviour under test:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;d558792f-d77e-4b54-bce6-dd0b4c4df410&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">val fixture = createFixture()

fixture.givenStored(customer)

assertThat(fixture.repository.findById(customer.id))
    .isEqualTo(CustomerLookup.Found(customer))</code></pre></div><p>The same separation works in the other direction. A save example should not need <code>findById</code> to observe the result:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;d55bcadc-0724-4105-b536-3ae0080327dc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">val fixture = createFixture()

fixture.repository.save(customer)

assertThat(fixture.storedCustomerById(customer.id))
    .isEqualTo(CustomerLookup.Found(customer))</code></pre></div><h2>Testing two implementations with the same role test</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Hsyd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35a43ea-929e-4192-8c28-c5d2763826e6_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Hsyd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35a43ea-929e-4192-8c28-c5d2763826e6_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Hsyd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35a43ea-929e-4192-8c28-c5d2763826e6_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Hsyd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35a43ea-929e-4192-8c28-c5d2763826e6_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Hsyd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35a43ea-929e-4192-8c28-c5d2763826e6_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Hsyd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35a43ea-929e-4192-8c28-c5d2763826e6_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b35a43ea-929e-4192-8c28-c5d2763826e6_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:876184,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35a43ea-929e-4192-8c28-c5d2763826e6_1024x559.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_!Hsyd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35a43ea-929e-4192-8c28-c5d2763826e6_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Hsyd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35a43ea-929e-4192-8c28-c5d2763826e6_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Hsyd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35a43ea-929e-4192-8c28-c5d2763826e6_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Hsyd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35a43ea-929e-4192-8c28-c5d2763826e6_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>The in-memory implementation is small:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;50378474-4752-4044-aef6-08454715a122&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class InMemoryCustomerRepository(
    private val customers: MutableMap&lt;CustomerId, Customer&gt; = mutableMapOf()
) : CustomerRepository {

    override fun findById(id: CustomerId): CustomerLookup {
        val customer = customers[id]
            ?: return CustomerLookup.Missing(id)

        return CustomerLookup.Found(customer)
    }

    override fun save(customer: Customer) {
        customers[customer.id] = customer
    }
}</code></pre></div><p>Its concrete test declares that it must satisfy the role:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;cc3af9f8-65c1-4c78-82ff-c1651978d141&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class InMemoryCustomerRepositoryShould :
    CustomerRepositoryRoleShould {

    override fun createFixture(): CustomerRepositoryFixture =
        InMemoryCustomerRepositoryFixture()
}

private class InMemoryCustomerRepositoryFixture :
    CustomerRepositoryFixture {

    private val stored = mutableMapOf&lt;CustomerId, Customer&gt;()

    override val repository: CustomerRepository =
        InMemoryCustomerRepository(stored)

    override fun givenStored(vararg customers: Customer) {
        customers.forEach { customer -&gt;
            stored[customer.id] = customer
        }
    }

    override fun storedCustomerById(id: CustomerId): CustomerLookup {
        val customer = stored[id]
            ?: return CustomerLookup.Missing(id)

        return CustomerLookup.Found(customer)
    }
}</code></pre></div><p>Now add a PostgreSQL implementation:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;345ffbd0-b2e3-4c9b-b272-d2c6ecd31119&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class CustomerRow(
    val id: String,
    val name: String
)

interface CustomerRows {
    fun findById(id: String): CustomerRow?
    fun insert(row: CustomerRow)
    fun upsert(row: CustomerRow)
}

class PostgresCustomerRepository(
    private val rows: CustomerRows
) : CustomerRepository {

    override fun findById(id: CustomerId): CustomerLookup {
        val row = rows.findById(id.value)
            ?: return CustomerLookup.Missing(id)

        return PostgresCustomerLookupMapper.toCustomerLookup(row)
    }

    override fun save(customer: Customer) {
        rows.insert(
            CustomerRow(
                id = customer.id.value,
                name = customer.name
            )
        )
    }
}

private object PostgresCustomerLookupMapper {

    fun toCustomerLookup(row: CustomerRow): CustomerLookup =
        CustomerLookup.Found(
            Customer(
                id = CustomerId(row.id),
                name = row.name
            )
        )
}</code></pre></div><p><code>PostgresCustomerLookupMapper</code> is deliberately not injected into the repository.</p><p>It is an internal implementation detail of the PostgreSQL adapter, not a peer of the <code>CustomerRepository</code> role. Extracting a collaborator does not automatically mean promoting it to the constructor boundary.</p><p>The role test protects the shared behaviour of <code>CustomerRepository</code>.</p><p>The mapper supports one implementation. As shown here, it is private and exercised through the repository. If it grows enough to deserve direct tests, that would be a reason to revisit its visibility and responsibilities, not a reason to place it in the shared role.</p><p>The PostgreSQL test uses the same suite:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;967bdd97-38f6-4dc1-aea3-438d444da685&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">class PostgresCustomerRepositoryShould :
    CustomerRepositoryRoleShould {

    override fun createFixture(): CustomerRepositoryFixture {
        val rows = TestDatabase.freshCustomerRows()
        return PostgresCustomerRepositoryFixture(rows)
    }
}

private class PostgresCustomerRepositoryFixture(
    private val rows: CustomerRows
) : CustomerRepositoryFixture {

    override val repository: CustomerRepository =
        PostgresCustomerRepository(rows)

    override fun givenStored(vararg customers: Customer) {
        customers.forEach { customer -&gt;
            rows.upsert(
                CustomerRow(
                    id = customer.id.value,
                    name = customer.name
                )
            )
        }
    }

    override fun storedCustomerById(id: CustomerId): CustomerLookup {
        val row = rows.findById(id.value)
            ?: return CustomerLookup.Missing(id)

        return CustomerLookup.Found(
            Customer(
                id = CustomerId(row.id),
                name = row.name
            )
        )
    }
}</code></pre></div><p>The concrete database harness is intentionally represented by <code>TestDatabase.freshCustomerRows()</code>. In a real test suite it must provide:</p><ul><li><p>A migrated schema</p></li><li><p>An empty customers table</p></li><li><p>Isolation between tests</p></li><li><p>The same database dialect used by the production adapter</p></li></ul><p>Possible implementations include a rolled-back transaction, table truncation, a fresh schema, a database sandbox, or a disposable container.</p><p>The minimal table needed by these examples is:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;445309e5-a45f-47e1-8abe-c2e151e22126&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">CREATE TABLE customers (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL
);</code></pre></div><p><code>PostgresCustomerRepositoryFixture</code> deliberately uses the table gateway directly. It prepares and inspects state at the adapter boundary without calling the role method currently under test.</p><p>That gives each role example a sharper failure signal:</p><pre><code><code>givenStored(...) -&gt; repository.findById(...)
repository.save(...) -&gt; storedCustomerById(...)</code></code></pre><p>If the lookup test fails, the failure points at lookup behaviour. If the save test fails, the failure points at save behaviour.</p><p>The shared suite should not know which isolation strategy is used. Fixture construction belongs to the concrete adapter test.</p><p>Both implementations now satisfy the same known behaviours.</p><p>The in-memory adapter can use a map. PostgreSQL can use a table gateway and an internal mapper. The suite does not care because those are implementation decisions, not role-level promises.</p><p>The behavioural specification is written once and run against every implementation that claims the abstraction.</p><h2>The missing <code>save</code> contract</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tvZ6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F548c007d-b045-4604-a2e8-d1d789dfac28_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tvZ6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F548c007d-b045-4604-a2e8-d1d789dfac28_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!tvZ6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F548c007d-b045-4604-a2e8-d1d789dfac28_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!tvZ6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F548c007d-b045-4604-a2e8-d1d789dfac28_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!tvZ6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F548c007d-b045-4604-a2e8-d1d789dfac28_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tvZ6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F548c007d-b045-4604-a2e8-d1d789dfac28_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/548c007d-b045-4604-a2e8-d1d789dfac28_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1150488,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F548c007d-b045-4604-a2e8-d1d789dfac28_1024x559.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_!tvZ6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F548c007d-b045-4604-a2e8-d1d789dfac28_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!tvZ6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F548c007d-b045-4604-a2e8-d1d789dfac28_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!tvZ6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F548c007d-b045-4604-a2e8-d1d789dfac28_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!tvZ6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F548c007d-b045-4604-a2e8-d1d789dfac28_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The current role suite still has a weakness.</p><p>It does not define what happens when the same customer id is saved twice.</p><p>That omission allows the in-memory and PostgreSQL implementations to pass while disagreeing about <code>save</code>.</p><p>Suppose a client renames a customer by saving a new representation with the same identity. Replacement semantics then become part of the role.</p><p>Add one example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;4f493a3f-2eb2-4383-b90c-13fd20a76f67&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">@Test
fun `replace previously stored customer with the same id`() {
    // Arrange
    val fixture = createFixture()
    val original = Customer(
        id = CustomerId("customer-1"),
        name = "Alice"
    )
    val renamed = original.copy(name = "Alicia")

    fixture.givenStored(original)

    // Act
    fixture.repository.save(renamed)

    // Assert
    assertThat(fixture.storedCustomerById(original.id))
        .isEqualTo(CustomerLookup.Found(renamed))
}</code></pre></div><p>This is the same before-and-after lesson applied to a new contract decision. The old shape would have been noisier:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;cc886705-67ff-4f0f-a4a5-4d0abd72fe73&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">repository.save(original)
repository.save(renamed)

assertThat(repository.findById(original.id))
    .isEqualTo(CustomerLookup.Found(renamed))</code></pre></div><p>That test checks an important history property, but it can still fail through lookup. The fixture-based version keeps the observed behaviour focused on what <code>save</code> must do when the identity already exists.</p><p>The in-memory implementation passes. The PostgreSQL implementation fails because the second insert violates the unique key.</p><p>The failed test reveals a missing contract decision.</p><p>Possible responses include:</p><ol><li><p>Define <code>save</code> as an upsert and change PostgreSQL</p></li><li><p>Define duplicate identity as an error and change the in-memory fake</p></li><li><p>Rename <code>save</code> to <code>add</code> if creation is the real responsibility</p></li><li><p>Separate <code>add</code> and <code>replace</code></p></li><li><p>Introduce optimistic concurrency if silent replacement would lose changes</p></li><li><p>Admit that the implementations do not satisfy the same role</p></li></ol><p>The role test should not invent the answer merely to make implementations equal. The decision comes from client needs and domain rules.</p><p>If last-write-wins replacement is the intended contract, PostgreSQL can implement an upsert:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;1af02670-5b8d-4f5f-aadd-ad6e52ef2445&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">override fun save(customer: Customer) {
    rows.upsert(
        CustomerRow(
            id = customer.id.value,
            name = customer.name
        )
    )
}</code></pre></div><p>The production adapter now satisfies the chosen role.</p><p>This does not make the fake the source of truth. The client requirement and the resulting specification are the source of truth. The fake happened to implement the selected behaviour first.</p><p>The example also demonstrates the limit of role testing:</p><blockquote><p>A shared suite is only as complete as the client-visible behaviours it expresses.</p></blockquote><p>Green tests meant that both implementations satisfied the initial examples. They did not mean that every semantic question had already been answered.</p><h2>Keeping test doubles aligned with real adapters</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AYG6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F244ab47b-bbca-4f42-9bca-8cb8ecd4ad8f_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AYG6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F244ab47b-bbca-4f42-9bca-8cb8ecd4ad8f_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!AYG6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F244ab47b-bbca-4f42-9bca-8cb8ecd4ad8f_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!AYG6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F244ab47b-bbca-4f42-9bca-8cb8ecd4ad8f_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!AYG6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F244ab47b-bbca-4f42-9bca-8cb8ecd4ad8f_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AYG6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F244ab47b-bbca-4f42-9bca-8cb8ecd4ad8f_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/244ab47b-bbca-4f42-9bca-8cb8ecd4ad8f_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:522197,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F244ab47b-bbca-4f42-9bca-8cb8ecd4ad8f_1024x559.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_!AYG6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F244ab47b-bbca-4f42-9bca-8cb8ecd4ad8f_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!AYG6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F244ab47b-bbca-4f42-9bca-8cb8ecd4ad8f_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!AYG6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F244ab47b-bbca-4f42-9bca-8cb8ecd4ad8f_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!AYG6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F244ab47b-bbca-4f42-9bca-8cb8ecd4ad8f_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>An in-memory repository is a fake: a working but simplified implementation of a dependency.</p><p>Fakes are useful because they are fast, deterministic, and easy to construct:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;8197e35f-1dfe-4571-b7f2-92a1bbdcc0da&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">val customers = InMemoryCustomerRepository()</code></pre></div><p>Those strengths also make them dangerous. A fake can become a fantasy object that behaves conveniently in tests but cannot be substituted by production.</p><p>Common divergences include:</p><ul><li><p>A map overwrites duplicate keys while SQL rejects them</p></li><li><p>A fake accepts values rejected by database constraints</p></li><li><p>A fake returns the exact stored instance while production reconstructs data</p></li><li><p>A fake ignores serialization and representation loss</p></li><li><p>A fake offers immediate consistency while a remote store is eventually consistent</p></li><li><p>A fake never fails</p></li><li><p>A fake is not thread-safe while production use is concurrent</p></li><li><p>A fake claims persistence but loses everything when the process ends</p></li></ul><p>A role suite reduces drift for the behaviours included in the contract.</p><p>The fake should not be judged by this rule:</p><blockquote><p>Make the fake reproduce PostgreSQL.</p></blockquote><p>The role contract asks for this:</p><blockquote><p>Make the fake and PostgreSQL honour the same promises made by <code>CustomerRepository</code>.</p></blockquote><p>Everything outside those promises may differ.</p><p>That separation prevents two opposite mistakes.</p><p>The first is an unrealistically weak fake:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;006cf5f0-b455-4639-bceb-e959e9b169d7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class AlwaysSuccessfulCustomerRepository :
    CustomerRepository {

    override fun findById(id: CustomerId): CustomerLookup =
        CustomerLookup.Missing(id)

    override fun save(customer: Customer) {
        // Pretend the operation succeeded.
    }
}</code></pre></div><p>Client tests using this fake can pass even though saved data can never be found.</p><p>The second mistake is recreating the infrastructure inside the fake. If the in-memory repository implements SQL locking, database transactions, network retries, connection pools, and persistence formats, it is no longer lightweight.</p><p>Role tests identify the middle ground:</p><ul><li><p>Reproduce the shared semantics</p></li><li><p>Omit implementation-specific mechanics</p></li><li><p>Test the real adapter separately</p></li></ul><p>The resulting test strategy has different suites protecting different risks:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qCVZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86564524-6f02-4cf6-8a29-05a007e207c5_1532x652.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qCVZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86564524-6f02-4cf6-8a29-05a007e207c5_1532x652.png 424w, https://substackcdn.com/image/fetch/$s_!qCVZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86564524-6f02-4cf6-8a29-05a007e207c5_1532x652.png 848w, https://substackcdn.com/image/fetch/$s_!qCVZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86564524-6f02-4cf6-8a29-05a007e207c5_1532x652.png 1272w, https://substackcdn.com/image/fetch/$s_!qCVZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86564524-6f02-4cf6-8a29-05a007e207c5_1532x652.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qCVZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86564524-6f02-4cf6-8a29-05a007e207c5_1532x652.png" width="1456" height="620" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/86564524-6f02-4cf6-8a29-05a007e207c5_1532x652.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:620,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:65682,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86564524-6f02-4cf6-8a29-05a007e207c5_1532x652.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_!qCVZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86564524-6f02-4cf6-8a29-05a007e207c5_1532x652.png 424w, https://substackcdn.com/image/fetch/$s_!qCVZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86564524-6f02-4cf6-8a29-05a007e207c5_1532x652.png 848w, https://substackcdn.com/image/fetch/$s_!qCVZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86564524-6f02-4cf6-8a29-05a007e207c5_1532x652.png 1272w, https://substackcdn.com/image/fetch/$s_!qCVZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86564524-6f02-4cf6-8a29-05a007e207c5_1532x652.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><a href="https://mermaid.live/edit#pako:eNpVkU1rwzAMhv-K0bk57JrDoKQMehiENlDYsoOJlcQstoIir4zS_z7lYxs9WZaeV3qFbtCQQ8ihHeja9JbFVIc6GlNU7zUUg8coRnCSqYYPk2XP5kXzx5gFDMTfprWfqJVZcTpr5awt0BmmAc2UvOCfbEWWuFSwZHKpEU_RWGdHQd7a7OfB-zWVTSM2vvXNo4Vy5o7VYkSwY7u2ic5gdJlQps-j4qLsCe1grp597Db2yzPFoBtuo8uVhR107B3kwgl3EJCDnb9wm6EapMege-UaOmxtGlRfx7vKRhvfiMKvkil1PeStHSb9pdFZwYO3avgfUavIBaUokD_tAJ0X4tf1Jstp7j9H_Ipc">Open and edit this diagram in Mermaid Live</a></figcaption></figure></div><p>Fakes are a common use case, but not the whole technique. A filesystem repository and a PostgreSQL repository can both be production adapters. A sandbox payment gateway and a live gateway may both be production-like environments. Kafka and another broker may coexist during migration.</p><p>The relevant fact is that each implementation claims the same role.</p><h2>Using role tests to replace a dependency safely</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qJYU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F974739ba-ca80-4ec6-afbf-0d333f9e1e1a_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qJYU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F974739ba-ca80-4ec6-afbf-0d333f9e1e1a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!qJYU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F974739ba-ca80-4ec6-afbf-0d333f9e1e1a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!qJYU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F974739ba-ca80-4ec6-afbf-0d333f9e1e1a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!qJYU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F974739ba-ca80-4ec6-afbf-0d333f9e1e1a_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qJYU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F974739ba-ca80-4ec6-afbf-0d333f9e1e1a_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/974739ba-ca80-4ec6-afbf-0d333f9e1e1a_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1219385,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F974739ba-ca80-4ec6-afbf-0d333f9e1e1a_1024x559.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_!qJYU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F974739ba-ca80-4ec6-afbf-0d333f9e1e1a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!qJYU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F974739ba-ca80-4ec6-afbf-0d333f9e1e1a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!qJYU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F974739ba-ca80-4ec6-afbf-0d333f9e1e1a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!qJYU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F974739ba-ca80-4ec6-afbf-0d333f9e1e1a_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Role tests become especially useful when a dependency must be replaced.</p><p>Imagine a system coupled to a legacy customer library:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;aaf4b835-2d48-4dfa-ac27-58741f7f0344&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class LegacyCustomerRecord(
    val customerNumber: String,
    val displayName: String
)

interface LegacyCustomerStore {
    fun put(record: LegacyCustomerRecord)
    fun fetch(customerNumber: String): LegacyCustomerRecord?
}</code></pre></div><p>The team wants to migrate to a new service:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;8acd99cf-de7e-468b-b6a0-57974cea701b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class CustomerRequest(
    val id: String,
    val fullName: String
)

data class CustomerResponse(
    val id: String,
    val fullName: String
)

interface CustomerServiceClient {
    fun upsert(request: CustomerRequest)
    fun get(customerId: String): CustomerResponse?
}</code></pre></div><p>The APIs differ. Directly changing every client would mix several concerns:</p><ul><li><p>Understanding what clients actually need</p></li><li><p>Translating between data models</p></li><li><p>Implementing the new integration</p></li><li><p>Updating every use site</p></li><li><p>Deciding which legacy behaviours must be preserved</p></li></ul><p>A safer refactor introduces a stable role:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;63af6217-719e-4334-b4e2-a333ad24bfe1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerRepository {
    fun findById(id: CustomerId): CustomerLookup
    fun save(customer: Customer)
}</code></pre></div><p>The old dependency receives an adapter:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;a8642554-023a-4333-9454-e6cf5caef2b1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class LegacyCustomerRepository(
    private val legacyStore: LegacyCustomerStore
) : CustomerRepository {

    override fun findById(id: CustomerId): CustomerLookup {
        val record = legacyStore.fetch(id.value)
            ?: return CustomerLookup.Missing(id)

        return CustomerLookup.Found(
            Customer(
                id = CustomerId(record.customerNumber),
                name = record.displayName
            )
        )
    }

    override fun save(customer: Customer) {
        legacyStore.put(
            LegacyCustomerRecord(
                customerNumber = customer.id.value,
                displayName = customer.name
            )
        )
    }
}</code></pre></div><p>The new dependency receives another adapter:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;7603b470-3cf4-4945-92c2-fbf69192a5b2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class ServiceCustomerRepository(
    private val client: CustomerServiceClient
) : CustomerRepository {

    override fun findById(id: CustomerId): CustomerLookup {
        val response = client.get(id.value)
            ?: return CustomerLookup.Missing(id)

        return CustomerLookup.Found(
            Customer(
                id = CustomerId(response.id),
                name = response.fullName
            )
        )
    }

    override fun save(customer: Customer) {
        client.upsert(
            CustomerRequest(
                id = customer.id.value,
                fullName = customer.name
            )
        )
    }
}</code></pre></div><p>Both adapters run the same role suite:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;d4eb9289-be70-433f-99b4-ec4512b57cee&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class LegacyCustomerRepositoryShould :
    CustomerRepositoryRoleShould {

    override fun createFixture(): CustomerRepositoryFixture =
        LegacyCustomerRepositoryFixture(
            legacyStore = LegacyTestEnvironment.emptyStore()
        )
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;592fce8f-5a33-4c62-83db-6b188bece36c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class ServiceCustomerRepositoryShould :
    CustomerRepositoryRoleShould {

    override fun createFixture(): CustomerRepositoryFixture =
        ServiceCustomerRepositoryFixture(
            client = CustomerServiceSandbox.emptyClient()
        )
}</code></pre></div><p>The following fixtures show the contract shape. Each real project supplies the backing test environment.</p><p><code>LegacyCustomerRepositoryFixture</code> and <code>ServiceCustomerRepositoryFixture</code> follow the same pattern as the PostgreSQL fixture: they expose the adapter through <code>repository</code>, prepare state through the real dependency&#8217;s test environment, and inspect state without calling the role method currently under test.</p><p>These fixture names represent production-compatible test environments: an isolated instance of the legacy store and a sandbox, emulator, disposable service, or controlled integration environment for the replacement.</p><p>Using another hand-written fake underneath each adapter may make the suite faster, but it would not prove that the adapter translates the real dependency correctly. If a provider cannot run in the role suite, retain provider-specific integration or distributed contract tests beside it.</p><p>A gradual migration can then proceed in small steps:</p><ol><li><p>Identify the behaviour clients need from the old dependency</p></li><li><p>Introduce a role interface around those needs</p></li><li><p>Adapt the old dependency to the role</p></li><li><p>Write the shared role suite</p></li><li><p>Make the old adapter satisfy the suite</p></li><li><p>Implement the new adapter</p></li><li><p>Make the new adapter satisfy the same suite</p></li><li><p>Switch clients incrementally</p></li><li><p>Remove the old dependency when no clients remain</p></li></ol><p>This is closely related to Branch by Abstraction. Martin Fowler describes that technique as introducing an abstraction so an old and a new supplier can coexist while clients migrate gradually.</p><p>Role tests strengthen the technique by making the abstraction&#8217;s expected behaviour executable.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gXXd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed59a1f-2d8e-4573-ad53-42a12126187a_2274x508.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gXXd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed59a1f-2d8e-4573-ad53-42a12126187a_2274x508.png 424w, https://substackcdn.com/image/fetch/$s_!gXXd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed59a1f-2d8e-4573-ad53-42a12126187a_2274x508.png 848w, https://substackcdn.com/image/fetch/$s_!gXXd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed59a1f-2d8e-4573-ad53-42a12126187a_2274x508.png 1272w, https://substackcdn.com/image/fetch/$s_!gXXd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed59a1f-2d8e-4573-ad53-42a12126187a_2274x508.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gXXd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed59a1f-2d8e-4573-ad53-42a12126187a_2274x508.png" width="1456" height="325" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bed59a1f-2d8e-4573-ad53-42a12126187a_2274x508.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:325,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:74834,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed59a1f-2d8e-4573-ad53-42a12126187a_2274x508.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_!gXXd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed59a1f-2d8e-4573-ad53-42a12126187a_2274x508.png 424w, https://substackcdn.com/image/fetch/$s_!gXXd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed59a1f-2d8e-4573-ad53-42a12126187a_2274x508.png 848w, https://substackcdn.com/image/fetch/$s_!gXXd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed59a1f-2d8e-4573-ad53-42a12126187a_2274x508.png 1272w, https://substackcdn.com/image/fetch/$s_!gXXd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed59a1f-2d8e-4573-ad53-42a12126187a_2274x508.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption"><a href="https://mermaid.live/edit#pako:eNplkcFOwzAMhl8l8nlD4toDl047lR66G4SDSdwuUppUqcNUTXt3nBaYgJsdf_7z276CiZaggt7HizljYtV0OihVv2qovaPAs4Y3td8_qU6eTozvnlSdZ44jpY6mODuOaVEpehKy9HYr3gje0IBmUWhxYkq_yq2UW7qomdKHM_SHaTaJw13D0kTBUjDLF9JuMgURGx4NjeL2P3cqtmUysqtHNWfHtM70UL5YkS1uS3wU_EjIOZHqPQ4qJmViYOn1q0RmF4afncAOhuQsVJwy7UB2MmJJ4VrENPBZbGmoJLTUY_asQYebtE0YXmIcvztFeDhD1aOfJcuTRaaDwyHhHSmDpTrmwFA97oBsWf3zdsD1jrdP-a-YBQ">Open and edit this diagram in Mermaid Live</a></figcaption></figure></div><p>The role suite does not compare the two external APIs. It checks that both adapters present the same <code>CustomerRepository</code> behaviour to local clients.</p><p>Role testing does not usually verify a contract against two unrelated interfaces. It verifies one role contract against the implementations or adapters that claim that role.</p><p>If the old and new dependencies expose different APIs, adapters translate both into the stable role.</p><p>During migration, the system may also run both implementations against production-like traffic and compare selected observations. That is useful, but it is differential testing or shadow traffic, not the role test itself.</p><p>The techniques complement one another:</p><ul><li><p>The role suite states what must remain true</p></li><li><p>Characterization tests reveal what the old dependency currently does</p></li><li><p>Differential tests find disagreements between old and new implementations</p></li><li><p>Integration tests prove that each real dependency is wired correctly</p></li><li><p>Feature flags or controlled routing migrate clients gradually</p></li></ul><p>Role tests are valuable because they prevent &#8220;the old dependency did it&#8221; from becoming the only definition of correctness.</p><h2>Replacing an OpenAPI-generated client without breaking local clients</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5_Bt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffac2230c-b7a4-46ed-9fe9-59cdab54ad7a_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5_Bt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffac2230c-b7a4-46ed-9fe9-59cdab54ad7a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!5_Bt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffac2230c-b7a4-46ed-9fe9-59cdab54ad7a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!5_Bt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffac2230c-b7a4-46ed-9fe9-59cdab54ad7a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!5_Bt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffac2230c-b7a4-46ed-9fe9-59cdab54ad7a_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5_Bt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffac2230c-b7a4-46ed-9fe9-59cdab54ad7a_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fac2230c-b7a4-46ed-9fe9-59cdab54ad7a_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1102273,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffac2230c-b7a4-46ed-9fe9-59cdab54ad7a_1024x559.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_!5_Bt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffac2230c-b7a4-46ed-9fe9-59cdab54ad7a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!5_Bt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffac2230c-b7a4-46ed-9fe9-59cdab54ad7a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!5_Bt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffac2230c-b7a4-46ed-9fe9-59cdab54ad7a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!5_Bt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffac2230c-b7a4-46ed-9fe9-59cdab54ad7a_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Generated clients are useful. An <em>OpenAPI Specification</em> (OAS) document describes provider-facing details: paths, operations, schemas, parameters, responses, and protocol errors.</p><p>The risk appears when application code starts speaking generated-client language directly.</p><p>Imagine checkout code coupled to a generated shipping client:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;77e96d5b-33e4-45af-bda8-222a98ac1a47&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface GeneratedShippingQuotesApi {
    fun quote(request: GeneratedShippingQuoteRequest): GeneratedShippingQuoteResponse
}

data class GeneratedShippingQuoteRequest(
    val destinationCountry: String,
    val weightInGrams: Int
)

data class GeneratedShippingQuoteResponse(
    val available: Boolean,
    val carrier: String?,
    val priceInCents: Long?
)

class CheckoutShippingOptions(
    private val shippingApi: GeneratedShippingQuotesApi
) {
    fun quoteFor(
        destinationCountry: String,
        weightInGrams: Int
    ): String {
        val response = shippingApi.quote(
            GeneratedShippingQuoteRequest(
                destinationCountry = destinationCountry,
                weightInGrams = weightInGrams
            )
        )

        if (!response.available) {
            return "No shipping available"
        }

        return "${requireNotNull(response.carrier)}: ${requireNotNull(response.priceInCents)} cents"
    }
}</code></pre></div><p>The generator is not the problem. The transport contract has become the local application role.</p><p>Checkout needs a smaller role:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;9d26445a-c19d-47b2-a65a-d0373a9fdb11&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class CountryCode(val value: String) {
    init {
        require(value.matches(Regex("[A-Z]{2}"))) {
            "Country code must use two uppercase letters."
        }
    }
}

data class ShippingRequest(
    val destinationCountry: CountryCode,
    val weightInGrams: Int
) {
    init {
        require(weightInGrams &gt; 0) {
            "Weight must be greater than zero."
        }
    }
}

data class ShippingQuote(
    val carrier: String,
    val priceInCents: Long
)

sealed interface ShippingQuoteResult {
    data class Quoted(val quote: ShippingQuote) : ShippingQuoteResult
    data class UnsupportedDestination(val country: CountryCode) : ShippingQuoteResult
}

interface ShippingQuotes {
    fun quoteFor(request: ShippingRequest): ShippingQuoteResult
}</code></pre></div><p>The shared role suite protects local behaviour, not OpenAPI protocol details:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;29c30d4c-9a43-487a-82f0-c7ed0d614e4e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface ShippingQuotesFixture {
    val shippingQuotes: ShippingQuotes

    fun givenAvailableQuote(
        request: ShippingRequest,
        quote: ShippingQuote
    )

    fun givenUnsupportedDestination(country: CountryCode)
}

interface ShippingQuotesRoleShould {

    fun createFixture(): ShippingQuotesFixture

    @Test
    fun `return a quote for a supported destination and weight`() {
        val fixture = createFixture()
        val request = ShippingRequest(
            destinationCountry = CountryCode("DE"),
            weightInGrams = 1200
        )
        val quote = ShippingQuote(
            carrier = "DHL",
            priceInCents = 899
        )

        fixture.givenAvailableQuote(request, quote)

        val result = fixture.shippingQuotes.quoteFor(request)

        assertThat(result)
            .isEqualTo(ShippingQuoteResult.Quoted(quote))
    }

    @Test
    fun `report unsupported destination in local domain language`() {
        val fixture = createFixture()
        val country = CountryCode("AQ")
        val request = ShippingRequest(
            destinationCountry = country,
            weightInGrams = 1200
        )

        fixture.givenUnsupportedDestination(country)

        val result = fixture.shippingQuotes.quoteFor(request)

        assertThat(result)
            .isEqualTo(ShippingQuoteResult.UnsupportedDestination(country))
    }
}</code></pre></div><p>The old adapter can wrap the generated client:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;7a1be9e1-707e-4421-b932-a0401cbeff47&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class GeneratedClientShippingQuotes(
    private val api: GeneratedShippingQuotesApi
) : ShippingQuotes {

    override fun quoteFor(request: ShippingRequest): ShippingQuoteResult {
        val response = api.quote(
            GeneratedShippingQuoteRequest(
                destinationCountry = request.destinationCountry.value,
                weightInGrams = request.weightInGrams
            )
        )

        if (!response.available) {
            return ShippingQuoteResult.UnsupportedDestination(
                request.destinationCountry
            )
        }

        return ShippingQuoteResult.Quoted(
            ShippingQuote(
                carrier = requireNotNull(response.carrier),
                priceInCents = requireNotNull(response.priceInCents)
            )
        )
    }
}</code></pre></div><p>The replacement can be a handwritten adapter:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;6ff8fa9c-e2d0-447e-9df4-8c12d8bcfb31&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface ShippingQuoteTransport {
    fun fetchQuote(request: ShippingRequest): ShippingQuoteResult
}

class ValidatedHttpShippingQuotes(
    private val transport: ShippingQuoteTransport
) : ShippingQuotes {

    override fun quoteFor(request: ShippingRequest): ShippingQuoteResult {
        return transport.fetchQuote(request)
    }
}</code></pre></div><p>Both adapters run the same <code>ShippingQuotesRoleShould</code> suite.</p><ul><li><p>The OAS contract describes the provider API</p></li><li><p>The role test describes the local application role</p></li><li><p>The generated client and the handwritten adapter are implementation choices behind that role</p></li></ul><p>The role suite does not replace provider contract tests, schema checks, authentication tests, retry tests, timeout tests, or real integration tests. It protects the meaning promised to application code while provider protocol concerns stay at the provider boundary.</p><h2>A Strategy example during a refactor</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3fUM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91c64b5-0516-44e8-942e-40734a5bb7c7_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3fUM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91c64b5-0516-44e8-942e-40734a5bb7c7_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!3fUM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91c64b5-0516-44e8-942e-40734a5bb7c7_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!3fUM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91c64b5-0516-44e8-942e-40734a5bb7c7_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!3fUM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91c64b5-0516-44e8-942e-40734a5bb7c7_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3fUM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91c64b5-0516-44e8-942e-40734a5bb7c7_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e91c64b5-0516-44e8-942e-40734a5bb7c7_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1022157,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91c64b5-0516-44e8-942e-40734a5bb7c7_1024x559.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_!3fUM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91c64b5-0516-44e8-942e-40734a5bb7c7_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!3fUM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91c64b5-0516-44e8-942e-40734a5bb7c7_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!3fUM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91c64b5-0516-44e8-942e-40734a5bb7c7_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!3fUM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91c64b5-0516-44e8-942e-40734a5bb7c7_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The Strategy pattern defines a family of algorithms behind a common interface so a client can select or replace the algorithm without owning its implementation details.</p><p>That makes Strategy a natural place for role tests, but it introduces an important distinction.</p><p>Sometimes strategies are intentionally different:</p><pre><code><code>RoadRouteStrategy and WalkingRouteStrategy</code></code></pre><p>They receive the same origin and destination but should produce different routes. A shared role suite can protect common properties, such as returning a connected route with the requested endpoints. It should not require equal routes.</p><p>In other cases, a new Strategy is intended as a behavioural replacement for an old one. Then the role suite can protect the complete business behaviour that must survive the refactor.</p><p>Consider a checkout that delegates pricing:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;cfa8c96a-f138-4eb7-aa3f-a6916bf19e34&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface PriceCalculationStrategy {
    fun calculatePrice(order: Order): Money
}</code></pre></div><p>The example uses a deliberately small, single-currency <code>Money</code> value so that the pricing rules remain visible:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;6e0645e6-7180-4225-8980-29ac292f2f66&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class Money private constructor(
    private val cents: Long
) : Comparable&lt;Money&gt; {

    init {
        require(cents &gt;= 0) {
            "Money cannot be negative."
        }
    }

    fun discountBy(percentage: Int): Money {
        require(percentage in 0..100) {
            "Discount percentage must be between 0 and 100."
        }

        val discountInCents = cents * percentage / 100
        return Money(cents - discountInCents)
    }

    override fun compareTo(other: Money): Int =
        cents.compareTo(other.cents)

    companion object {
        fun euros(amount: String): Money {
            val decimal = amount.toBigDecimal()
                .setScale(2)

            return Money(
                decimal.movePointRight(2)
                    .longValueExact()
            )
        }

        fun zero(): Money = Money(0)
    }
}

enum class CustomerType {
    REGULAR,
    PREFERRED
}

data class Order(
    val customerType: CustomerType,
    val subtotal: Money
)</code></pre></div><p>Production money handling may require several currencies, explicit rounding rules, overflow protection, and auditability. The pricing role below only needs enough money behaviour to make the strategy rules visible.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;52b8c084-f448-46ed-aaf8-b5c462f760db&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class Checkout(
    private val priceCalculation: PriceCalculationStrategy
) {
    fun totalFor(order: Order): Money =
        priceCalculation.calculatePrice(order)
}</code></pre></div><p>The current strategy contains legacy conditionals:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;4ee73f87-09fa-4a24-94e3-5d8a64e2e922&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class LegacyPriceCalculationStrategy :
    PriceCalculationStrategy {

    override fun calculatePrice(order: Order): Money {
        if (order.customerType == CustomerType.PREFERRED) {
            return order.subtotal.discountBy(10)
        }

        return order.subtotal
    }
}</code></pre></div><p>The team wants to replace it with a rule-based design:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;1c5e1482-28d8-417e-a1aa-c86d67d50491&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface PricingRule {
    fun appliesTo(order: Order): Boolean
    fun priceFor(order: Order): Money
}

class PreferredCustomerPricingRule : PricingRule {

    override fun appliesTo(order: Order): Boolean =
        order.customerType == CustomerType.PREFERRED

    override fun priceFor(order: Order): Money =
        order.subtotal.discountBy(10)
}

class RuleBasedPriceCalculationStrategy(
    private val rules: List&lt;PricingRule&gt;
) : PriceCalculationStrategy {

    override fun calculatePrice(order: Order): Money {
        val applicableRule = rules.firstOrNull {
            it.appliesTo(order)
        }

        return applicableRule?.priceFor(order)
            ?: order.subtotal
    }
}</code></pre></div><p>The role suite expresses the rules clients rely on:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;d7eb359e-7ddb-4e0c-a311-33a768431ee8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

interface PriceCalculationStrategyRoleShould {

    fun createStrategy(): PriceCalculationStrategy

    @Test
    fun `charge subtotal to a regular customer`() {
        val strategy = createStrategy()
        val order = Order(
            customerType = CustomerType.REGULAR,
            subtotal = Money.euros("100.00")
        )

        val total = strategy.calculatePrice(order)

        assertThat(total)
            .isEqualTo(Money.euros("100.00"))
    }

    @Test
    fun `apply ten percent discount to a preferred customer`() {
        val strategy = createStrategy()
        val order = Order(
            customerType = CustomerType.PREFERRED,
            subtotal = Money.euros("100.00")
        )

        val total = strategy.calculatePrice(order)

        assertThat(total)
            .isEqualTo(Money.euros("90.00"))
    }

    @Test
    fun `keep a zero subtotal at zero`() {
        val strategy = createStrategy()
        val order = Order(
            customerType = CustomerType.PREFERRED,
            subtotal = Money.zero()
        )

        val total = strategy.calculatePrice(order)

        assertThat(total)
            .isEqualTo(Money.zero())
    }
}</code></pre></div><p>Both strategies claim the role:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;957b9f20-6088-46de-8f89-60cb9f5b7946&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class LegacyPriceCalculationStrategyShould :
    PriceCalculationStrategyRoleShould {

    override fun createStrategy(): PriceCalculationStrategy =
        LegacyPriceCalculationStrategy()
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;d6ef29dc-93d6-42d4-81e2-01c494521a83&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class RuleBasedPriceCalculationStrategyShould :
    PriceCalculationStrategyRoleShould {

    override fun createStrategy(): PriceCalculationStrategy =
        RuleBasedPriceCalculationStrategy(
            rules = listOf(
                PreferredCustomerPricingRule()
            )
        )
}</code></pre></div><p>The static structure is simple: <code>Checkout</code> depends on the role, while the role suite verifies both strategy implementations independently.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yd3b!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42803160-6e16-4557-8539-79d48390e6d1_1755x1000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yd3b!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42803160-6e16-4557-8539-79d48390e6d1_1755x1000.png 424w, https://substackcdn.com/image/fetch/$s_!yd3b!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42803160-6e16-4557-8539-79d48390e6d1_1755x1000.png 848w, https://substackcdn.com/image/fetch/$s_!yd3b!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42803160-6e16-4557-8539-79d48390e6d1_1755x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!yd3b!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42803160-6e16-4557-8539-79d48390e6d1_1755x1000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yd3b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42803160-6e16-4557-8539-79d48390e6d1_1755x1000.png" width="1456" height="830" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/42803160-6e16-4557-8539-79d48390e6d1_1755x1000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:830,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:109693,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42803160-6e16-4557-8539-79d48390e6d1_1755x1000.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_!yd3b!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42803160-6e16-4557-8539-79d48390e6d1_1755x1000.png 424w, https://substackcdn.com/image/fetch/$s_!yd3b!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42803160-6e16-4557-8539-79d48390e6d1_1755x1000.png 848w, https://substackcdn.com/image/fetch/$s_!yd3b!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42803160-6e16-4557-8539-79d48390e6d1_1755x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!yd3b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42803160-6e16-4557-8539-79d48390e6d1_1755x1000.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><a href="https://mermaid.live/edit#pako:eNqVU8FuwjAM_ZXIJ9Cg0q4V6mFMOw1pgtvUi5W4bbQ0Rm4yCTH-fSkqdBcKOzlO3nt2XuIjaDYEOWiHXfdqsRZsS6_UOVfrhvQXx6CO_Z5ST4EDujeWGYshmasNezr0Z6eR9CFW0xqdjg6DZb8LgoHqw0VktbI-kFSoqSgGXT3A6UyeUn-nGvXhVo0Rt42OXrAjcx96C7FlR7uGozNj64G6MHYtlIAX-GyupmqlO_ThaulyWUzib9q4-smyB2yY5j9kzwPGZFlxpxeVq28SW1nq_qF5t78_srCAWqyBPEikBbQkLfYpnF-thNBQSyXkaWmowuhCCaU_Jdoe_Sdze2EKx7qBvELXpSzuTao0zMQVQj59zTVHHyB_XgAZG1g2wxD14fQLTe0zvQ">Open and edit this diagram in Mermaid Live</a></figcaption></figure></div><p>Now the team can refactor the internal design without changing what <code>Checkout</code> is allowed to assume.</p><p>The suite protects business meaning:</p><ul><li><p>Regular customers pay the subtotal</p></li><li><p>Preferred customers receive the agreed discount</p></li><li><p>A zero subtotal remains zero</p></li></ul><p>It does not protect implementation details:</p><ul><li><p><code>if</code> statements</p></li><li><p>Rule ordering internals not visible to clients</p></li><li><p>The number of rule objects</p></li><li><p>Whether percentages are calculated through one method or another</p></li></ul><p>The suite protects business meaning without turning the old implementation into the oracle.</p><p>If the legacy strategy contains an accidental bug for one unusual order, the team must decide whether that behaviour is contractual. A characterization or differential test may expose the disagreement. The role suite contains the decision about what should survive.</p><blockquote><p>Strategies are interchangeable only relative to the properties promised by their shared role.</p></blockquote><p>If <code>RoadRouteStrategy</code> and <code>WalkingRouteStrategy</code> intentionally optimize different outcomes, their role test should express common route validity, not equal paths. If <code>RuleBasedPriceCalculationStrategy</code> is a drop-in replacement for the legacy pricing strategy, its suite can express the complete pricing rules that clients require.</p><h2>Role tests, characterization tests, and differential tests</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3sdD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F920b2670-a42c-4631-af5d-e5221e7657ca_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3sdD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F920b2670-a42c-4631-af5d-e5221e7657ca_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!3sdD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F920b2670-a42c-4631-af5d-e5221e7657ca_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!3sdD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F920b2670-a42c-4631-af5d-e5221e7657ca_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!3sdD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F920b2670-a42c-4631-af5d-e5221e7657ca_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3sdD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F920b2670-a42c-4631-af5d-e5221e7657ca_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/920b2670-a42c-4631-af5d-e5221e7657ca_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:679969,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F920b2670-a42c-4631-af5d-e5221e7657ca_1024x559.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_!3sdD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F920b2670-a42c-4631-af5d-e5221e7657ca_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!3sdD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F920b2670-a42c-4631-af5d-e5221e7657ca_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!3sdD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F920b2670-a42c-4631-af5d-e5221e7657ca_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!3sdD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F920b2670-a42c-4631-af5d-e5221e7657ca_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>During a replacement, teams often need three related activities. They should not collapse into one another.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3PLT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39c040c8-a26c-495d-94b7-555ab62662df_688x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3PLT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39c040c8-a26c-495d-94b7-555ab62662df_688x188.png 424w, https://substackcdn.com/image/fetch/$s_!3PLT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39c040c8-a26c-495d-94b7-555ab62662df_688x188.png 848w, https://substackcdn.com/image/fetch/$s_!3PLT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39c040c8-a26c-495d-94b7-555ab62662df_688x188.png 1272w, https://substackcdn.com/image/fetch/$s_!3PLT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39c040c8-a26c-495d-94b7-555ab62662df_688x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3PLT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39c040c8-a26c-495d-94b7-555ab62662df_688x188.png" width="728" height="198.93023255813952" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/39c040c8-a26c-495d-94b7-555ab62662df_688x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:188,&quot;width&quot;:688,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:53042,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39c040c8-a26c-495d-94b7-555ab62662df_688x188.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_!3PLT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39c040c8-a26c-495d-94b7-555ab62662df_688x188.png 424w, https://substackcdn.com/image/fetch/$s_!3PLT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39c040c8-a26c-495d-94b7-555ab62662df_688x188.png 848w, https://substackcdn.com/image/fetch/$s_!3PLT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39c040c8-a26c-495d-94b7-555ab62662df_688x188.png 1272w, https://substackcdn.com/image/fetch/$s_!3PLT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39c040c8-a26c-495d-94b7-555ab62662df_688x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Observed legacy behaviour can inform the role contract. It should not become the role contract automatically.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!prFx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73a2bdf1-dbdf-4af2-865b-cdea28258114_4146x418.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!prFx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73a2bdf1-dbdf-4af2-865b-cdea28258114_4146x418.png 424w, https://substackcdn.com/image/fetch/$s_!prFx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73a2bdf1-dbdf-4af2-865b-cdea28258114_4146x418.png 848w, https://substackcdn.com/image/fetch/$s_!prFx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73a2bdf1-dbdf-4af2-865b-cdea28258114_4146x418.png 1272w, https://substackcdn.com/image/fetch/$s_!prFx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73a2bdf1-dbdf-4af2-865b-cdea28258114_4146x418.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!prFx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73a2bdf1-dbdf-4af2-865b-cdea28258114_4146x418.png" width="1456" height="147" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/73a2bdf1-dbdf-4af2-865b-cdea28258114_4146x418.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:147,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:85719,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73a2bdf1-dbdf-4af2-865b-cdea28258114_4146x418.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_!prFx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73a2bdf1-dbdf-4af2-865b-cdea28258114_4146x418.png 424w, https://substackcdn.com/image/fetch/$s_!prFx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73a2bdf1-dbdf-4af2-865b-cdea28258114_4146x418.png 848w, https://substackcdn.com/image/fetch/$s_!prFx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73a2bdf1-dbdf-4af2-865b-cdea28258114_4146x418.png 1272w, https://substackcdn.com/image/fetch/$s_!prFx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73a2bdf1-dbdf-4af2-865b-cdea28258114_4146x418.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption"><a href="https://mermaid.live/edit#pako:eNptkUFvwjAMhf-K5TMcdu1hF7pxYatUDkNbdzCJ20ZKE5SkoA3x3-e0IIS0W5782e_ZOaPymrHA1vqT6ikk2NSNA1h9NbgSTSpxML-UjHeQOKbY4Dcsl89QCVHtI4cja7DckfqBPfd0NH4MAuUp1USWQpasjGY49ZTARDAuscszyV7RckJrQWtvGZR3KZtfq_VU3Up1K6HEMGTolicT2znUJptVVoMZDpYHMZmiP0DvLx8CvfPpf2iX45q25ZAjkn1cey3VV-M0aBOpCzy1R5C1oiLLt8U38z67LMTvLtbzSXCBXTAaixRGXuDAYaAs8ZyhBlMvgxss5Km5pdHKJRp3kbYDuU_vh1tn8GPXY9GSjaLGg6bEpZFkdEfYaQ4rP7qExdMCWZvkw9v88dP_X_4AKB2pvQ">Open and edit this diagram in Mermaid Live</a></figcaption></figure></div><h3>Characterization tests</h3><p>Michael Feathers describes characterization tests as tests that document actual current behaviour so code can be changed more deterministically.</p><p>When a team does not yet understand a legacy implementation, a characterization test records what happens today:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;6f65ae3a-a70e-470f-8f33-4219d4a2dc79&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">@Test
fun `characterize current preferred customer pricing`() {
    val legacy = LegacyPriceCalculationStrategy()
    val order = Order(
        customerType = CustomerType.PREFERRED,
        subtotal = Money.euros("100.00")
    )

    val observedTotal = legacy.calculatePrice(order)

    assertThat(observedTotal)
        .isEqualTo(Money.euros("90.00"))
}</code></pre></div><p>Before the refactor, the test creates a concrete observation:</p><div class="callout-block" data-callout="true"><p>The legacy implementation currently gives preferred customers a ten percent discount.</p></div><p>It does not yet say:</p><div class="callout-block" data-callout="true"><p>Every valid pricing strategy must give preferred customers a ten percent discount.</p></div><p>That second statement requires a decision. The team may confirm that the behaviour is an intentional business rule, discover that it is an accidental bug, or decide that the rule should change.</p><p>Characterization tests are useful but dangerous as a source of truth. They capture what exists, including accidental behaviour.</p><h3>Differential tests</h3><p>Differential tests run the same examples through old and new implementations:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;be07fb40-2356-4ec0-8a05-ef633083fee5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">val oldResult = legacyStrategy.calculatePrice(order)
val newResult = ruleBasedStrategy.calculatePrice(order)

assertThat(newResult).isEqualTo(oldResult)</code></pre></div><p>Differential tests reveal disagreement across many generated or recorded inputs.</p><p>They are less useful for deciding which result is correct.</p><h3>Role tests</h3><p>Role tests encode the behaviour clients are allowed to require. If the team decides that the preferred-customer discount is contractual, the observation moves into the shared role suite:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;8e81d374-f5db-44cd-a211-c4f9fba77ed5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">@Test
fun `apply ten percent discount to a preferred customer`() {
    val strategy = createStrategy()
    val order = Order(
        customerType = CustomerType.PREFERRED,
        subtotal = Money.euros("100.00")
    )

    val total = strategy.calculatePrice(order)

    assertThat(total)
        .isEqualTo(Money.euros("90.00"))
}</code></pre></div><p>The expected value is the same as in the characterization test, but the oracle is different.</p><p>The characterization-test oracle:</p><div class="callout-block" data-callout="true"><p>The legacy strategy does this today.</p></div><p>The role-test oracle:</p><div class="callout-block" data-callout="true"><p>Every strategy that claims this role must do this.</p></div><p>Characterization tests can feed a role test, but they should not become the role test automatically.</p><p>During a dependency replacement, a strong workflow can use all three:</p><ol><li><p>Characterize the legacy implementation</p></li><li><p>Identify which observed behaviours are intentional</p></li><li><p>Place intentional shared behaviour in the role suite</p></li><li><p>Run the role suite against old and new adapters</p></li><li><p>Use differential testing on a wider dataset to find unexpected disagreements</p></li><li><p>Decide each disagreement using domain rules and client needs</p></li></ol><p>The workflow avoids two failure modes:</p><ul><li><p>Blindly copying every legacy behaviour</p></li><li><p>Rewriting the dependency without understanding what clients rely on</p></li></ul><h2>What should go into a role test?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YFlh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a51e535-653c-4cb2-80c4-0928ebb832b1_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YFlh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a51e535-653c-4cb2-80c4-0928ebb832b1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!YFlh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a51e535-653c-4cb2-80c4-0928ebb832b1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!YFlh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a51e535-653c-4cb2-80c4-0928ebb832b1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!YFlh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a51e535-653c-4cb2-80c4-0928ebb832b1_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YFlh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a51e535-653c-4cb2-80c4-0928ebb832b1_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6a51e535-653c-4cb2-80c4-0928ebb832b1_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1198998,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a51e535-653c-4cb2-80c4-0928ebb832b1_1024x559.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_!YFlh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a51e535-653c-4cb2-80c4-0928ebb832b1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!YFlh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a51e535-653c-4cb2-80c4-0928ebb832b1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!YFlh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a51e535-653c-4cb2-80c4-0928ebb832b1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!YFlh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a51e535-653c-4cb2-80c4-0928ebb832b1_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A shared-suite behaviour must be promised by every valid implementation.</p><p>A practical filter is:</p><blockquote><p>Could a client written only against this role reasonably depend on this behaviour?</p></blockquote><p>For <code>CustomerRepository</code>, that may include:</p><ul><li><p>A saved customer can be found by id</p></li><li><p>An unknown id produces <code>Missing</code></p></li><li><p>Customers with different ids remain independent</p></li><li><p>Relevant customer data survives persistence and reconstruction</p></li><li><p>Saving the same id has defined semantics</p></li><li><p>Invalid input is handled consistently, if input validation belongs to the role</p></li><li><p>Failures use a stable domain language, if clients are expected to react to them</p></li></ul><p>For <code>PriceCalculationStrategy</code>, it may include:</p><ul><li><p>Business rules that every drop-in pricing implementation must preserve</p></li><li><p>Boundary behaviour such as preserving a zero subtotal</p></li><li><p>Boundary behaviour around zero or discount limits</p></li></ul><p>Role tests often describe operation sequences rather than isolated methods:</p><pre><code><code>save customer
find customer</code></code></pre><pre><code><code>save original
save replacement
find identity</code></code></pre><pre><code><code>calculate regular order
calculate preferred order
preserve a zero subtotal</code></code></pre><p>The term behavioural contract matters because the suite describes interactions, not only isolated method returns.</p><p>Use preconditions, postconditions, invariants, failures, and history as prompts:</p><ul><li><p>Does one implementation reject an input the role accepts?</p></li><li><p>Does it return an outcome the role does not permit?</p></li><li><p>Does it throw an exception clients cannot handle?</p></li><li><p>Can it break an invariant?</p></li><li><p>Can a sequence of calls create an observation forbidden by the role?</p></li></ul><p>Do not add every conceivable case. Add behaviours clients are allowed to rely on and risks likely to cause semantic drift.</p><p>The suite should grow when the role becomes better understood.</p><h2>What should not go into a role test?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TOj3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93e072eb-613c-4dcc-84df-04ed7cce55aa_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TOj3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93e072eb-613c-4dcc-84df-04ed7cce55aa_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!TOj3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93e072eb-613c-4dcc-84df-04ed7cce55aa_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!TOj3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93e072eb-613c-4dcc-84df-04ed7cce55aa_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!TOj3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93e072eb-613c-4dcc-84df-04ed7cce55aa_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TOj3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93e072eb-613c-4dcc-84df-04ed7cce55aa_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/93e072eb-613c-4dcc-84df-04ed7cce55aa_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:599339,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93e072eb-613c-4dcc-84df-04ed7cce55aa_1024x559.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_!TOj3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93e072eb-613c-4dcc-84df-04ed7cce55aa_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!TOj3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93e072eb-613c-4dcc-84df-04ed7cce55aa_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!TOj3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93e072eb-613c-4dcc-84df-04ed7cce55aa_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!TOj3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93e072eb-613c-4dcc-84df-04ed7cce55aa_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A role test should not become a dump of every behaviour of every implementation.</p><p>These do not belong in a generic <code>CustomerRepositoryRoleShould</code>:</p><ul><li><p>Creating the customers table</p></li><li><p>Using a specific SQL index</p></li><li><p>Opening exactly one database connection</p></li><li><p>Retrying a PostgreSQL deadlock three times</p></li><li><p>Calling <code>PostgresCustomerLookupMapper</code></p></li><li><p>Serializing customers as JSON</p></li><li><p>Storing files under a particular directory</p></li><li><p>Using a mutable map internally</p></li><li><p>Calling an HTTP endpoint with a specific library</p></li></ul><p>Some of these behaviours need tests. They belong to the concrete adapter.</p><p>The same separation applies to Strategy:</p><ul><li><p>The role suite can protect pricing outcomes</p></li><li><p>It should not require <code>RuleBasedPriceCalculationStrategy</code> to evaluate rules through a particular collection operation</p></li><li><p>It should not require the legacy strategy to contain an <code>if</code></p></li></ul><p>The shared suite should also avoid accidental behaviour.</p><p>Suppose PostgreSQL returns rows in insertion order for a query without <code>ORDER BY</code>. If no client is promised that order, do not add it to the role suite merely because it happened in one test run.</p><p>Every shared assertion increases the contract surface. Once clients depend on it, changing it becomes more expensive.</p><p>The suite should capture intentional guarantees, not everything implementations happen to share.</p><p>There is another warning sign: implementation-specific branching inside the suite.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;31215874-c553-4d61-8c5c-1c739d559a64&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">fun expectedDuplicateBehaviour(): DuplicateBehaviour
fun expectedOrdering(): Ordering
fun expectedFailureType(): FailureType</code></pre></div><p>If every assertion changes according to the implementation, the suite no longer describes one role. It describes a family of incompatible behaviours.</p><p>The right response may be:</p><ul><li><p>Narrow the shared contract</p></li><li><p>Split the interface</p></li><li><p>Introduce separate roles</p></li><li><p>Accept that one implementation is not substitutable</p></li></ul><h2>Role tests and object-oriented design</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YG0o!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F353661c1-7f29-4da1-918d-4e201e19dba7_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YG0o!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F353661c1-7f29-4da1-918d-4e201e19dba7_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!YG0o!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F353661c1-7f29-4da1-918d-4e201e19dba7_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!YG0o!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F353661c1-7f29-4da1-918d-4e201e19dba7_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!YG0o!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F353661c1-7f29-4da1-918d-4e201e19dba7_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YG0o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F353661c1-7f29-4da1-918d-4e201e19dba7_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/353661c1-7f29-4da1-918d-4e201e19dba7_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1094757,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F353661c1-7f29-4da1-918d-4e201e19dba7_1024x559.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_!YG0o!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F353661c1-7f29-4da1-918d-4e201e19dba7_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!YG0o!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F353661c1-7f29-4da1-918d-4e201e19dba7_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!YG0o!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F353661c1-7f29-4da1-918d-4e201e19dba7_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!YG0o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F353661c1-7f29-4da1-918d-4e201e19dba7_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Role tests are not only a testing technique. They create design pressure.</p><p>They force a question that class-oriented designs can avoid:</p><blockquote><p>What role is this object playing for its clients?</p></blockquote><p>That question is stronger than:</p><blockquote><p>What class is this?</p></blockquote><p>A class is a code construct. A role is a responsibility in a collaboration.</p><p>Role tests fit naturally with object-oriented design. Objects collaborate through messages and make promises to their neighbours. A role test captures part of those promises.</p><p>Role tests can reveal weak abstractions:</p><ul><li><p>If the interface is hard to role-test, the role may be unclear</p></li><li><p>If each implementation needs exceptions to the shared suite, the interface may hide several concepts</p></li><li><p>If the suite needs implementation internals, the abstraction may be leaking</p></li><li><p>If the suite becomes enormous, the role may own too many responsibilities</p></li><li><p>If a fake cannot satisfy production semantics, the fake may be misleading</p></li><li><p>If production cannot satisfy the role promised to clients, the abstraction may be unrealistic</p></li></ul><p>The <code>PostgresCustomerLookupMapper</code> example illustrates a smaller boundary decision.</p><p>The mapper improves one adapter&#8217;s internal design. It is not part of the shared repository role. Clients should not know it exists, so the role suite ignores it.</p><p>The Strategy example illustrates another boundary.</p><p><code>Checkout</code> knows only <code>PriceCalculationStrategy</code>. It should not know whether pricing is implemented through conditionals, a rule collection, a decision table, or an external engine. The role test protects the result that <code>Checkout</code> can rely on.</p><p>Role tests also support evolvability.</p><p>When a new adapter or strategy is introduced, the suite provides immediate feedback about whether the existing abstraction is strong enough. When a dependency is replaced, the suite separates client promises from supplier details. When a test fake is used, the suite reduces the risk that tests execute against an easier but semantically false world.</p><p>Role tests do not magically produce good design. They make vague contracts harder to ignore.</p><h2>Common mistakes</h2><h3>Wrong oracle</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!b7de!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e31665f-1425-41c0-9010-981000df5c8a_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!b7de!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e31665f-1425-41c0-9010-981000df5c8a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!b7de!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e31665f-1425-41c0-9010-981000df5c8a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!b7de!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e31665f-1425-41c0-9010-981000df5c8a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!b7de!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e31665f-1425-41c0-9010-981000df5c8a_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!b7de!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e31665f-1425-41c0-9010-981000df5c8a_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7e31665f-1425-41c0-9010-981000df5c8a_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1028882,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e31665f-1425-41c0-9010-981000df5c8a_1024x559.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_!b7de!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e31665f-1425-41c0-9010-981000df5c8a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!b7de!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e31665f-1425-41c0-9010-981000df5c8a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!b7de!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e31665f-1425-41c0-9010-981000df5c8a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!b7de!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e31665f-1425-41c0-9010-981000df5c8a_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The production adapter is not automatically the truth. The fake is not automatically the truth. The legacy dependency is not automatically the truth.</p><p>The intentional role specification is the reference.</p><p>Characterization and differential tests can reveal legacy behaviour. They do not decide which behaviour deserves to become contractual.</p><h3>Overclaiming substitutability</h3><p>Passing examples is evidence for represented behaviour, not exhaustive proof.</p><p>State the boundary of the suite honestly.</p><h3>Implementation-detail assertions</h3><p>Tests such as &#8220;store the customer in a map,&#8221; &#8220;execute this SQL,&#8221; or &#8220;evaluate exactly three pricing rules&#8221; may be valid implementation tests. They are not role tests.</p><h3>Overgrown or inconsistent roles</h3><p>Persistence, search, exporting, reporting, and notification rarely form one coherent repository role.</p><p>Test friction can be design feedback.</p><p>If the suite expects replacement from one repository and duplicate rejection from another, it is not protecting one role.</p><p>Role tests are useful when a meaningful role and substitution pressure already exist. They are not a reason to create interfaces everywhere.</p><h3>Replacing other test types</h3><p>A PostgreSQL adapter can pass its role suite and still fail because of migrations, permissions, configuration, connection pools, or deployment.</p><p>An HTTP adapter can pass and still fail against the real provider.</p><p>Role tests are object-oriented contract tests for substitutability. Distributed contract tests use the same broad language for compatibility across an independently changing boundary. State which meaning is intended instead of treating either vocabulary as universal.</p><h3>Weak or over-realistic fakes</h3><p>A fake that ignores calls, always succeeds, or stores less information than the role promises can make client tests misleading.</p><p>A fake should reproduce shared semantics, not database engines, networks, or operational failure systems.</p><h3>Strategy equality</h3><p>Alternative strategies can intentionally produce different outputs.</p><p>The shared suite should protect the common role. Equal output is required only when one strategy is intended as a behavioural replacement for another.</p><h3>Fragile test mechanics</h3><p>Each implementation fixture must create a clean and known state. Shared tests are unreliable if state leaks between examples.</p><p>A test interface, abstract class, or shared-example feature is only the delivery mechanism.</p><p>Most mistakes come from choosing the wrong oracle, widening the role beyond client needs, or confusing shared semantics with implementation mechanics.</p><h2>When role tests are worth the cost</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4giz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4773a70-936e-4513-8a12-c32af767475d_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4giz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4773a70-936e-4513-8a12-c32af767475d_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!4giz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4773a70-936e-4513-8a12-c32af767475d_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!4giz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4773a70-936e-4513-8a12-c32af767475d_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!4giz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4773a70-936e-4513-8a12-c32af767475d_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4giz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4773a70-936e-4513-8a12-c32af767475d_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4773a70-936e-4513-8a12-c32af767475d_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:586392,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4773a70-936e-4513-8a12-c32af767475d_1024x559.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_!4giz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4773a70-936e-4513-8a12-c32af767475d_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!4giz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4773a70-936e-4513-8a12-c32af767475d_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!4giz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4773a70-936e-4513-8a12-c32af767475d_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!4giz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4773a70-936e-4513-8a12-c32af767475d_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Role tests add work:</p><ul><li><p>A shared suite must be named and maintained</p></li><li><p>Every implementation needs an isolated fixture</p></li><li><p>The team must decide which behaviours are genuinely shared</p></li><li><p>Real adapters can make the suite slower</p></li></ul><p>They are especially valuable when:</p><ul><li><p>A fake is used heavily in client tests</p></li><li><p>Several implementations already exist</p></li><li><p>A new Strategy is intended as a drop-in replacement</p></li><li><p>A database, library, framework, or external service is being replaced</p></li><li><p>Old and new adapters must coexist during migration</p></li><li><p>Different deployments choose different adapters</p></li><li><p>Semantic drift would be expensive</p></li><li><p>The abstraction is important enough to deserve explicit behavioural documentation</p></li></ul><p>They may add little value when:</p><ul><li><p>There is one implementation and no realistic substitution pressure</p></li><li><p>The interface exists only because a framework requires it</p></li><li><p>The abstraction has almost no meaningful behaviour</p></li><li><p>The implementations intentionally promise different semantics</p></li><li><p>The role suite would repeat behaviour already protected more clearly through a stable public boundary</p></li></ul><p>Sometimes the difficulty of writing a role test is evidence that the interface itself is not carrying enough design value.</p><p>Removing, narrowing, or splitting the interface may be better than surrounding it with more test architecture.</p><h2>Conclusion</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wkci!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F266da9d4-bcf5-44fc-a059-b3d8812ec7e1_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wkci!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F266da9d4-bcf5-44fc-a059-b3d8812ec7e1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!wkci!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F266da9d4-bcf5-44fc-a059-b3d8812ec7e1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!wkci!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F266da9d4-bcf5-44fc-a059-b3d8812ec7e1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!wkci!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F266da9d4-bcf5-44fc-a059-b3d8812ec7e1_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wkci!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F266da9d4-bcf5-44fc-a059-b3d8812ec7e1_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/266da9d4-bcf5-44fc-a059-b3d8812ec7e1_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:787570,&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://emmanuelvalverderamos.substack.com/i/203212220?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F266da9d4-bcf5-44fc-a059-b3d8812ec7e1_1024x559.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_!wkci!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F266da9d4-bcf5-44fc-a059-b3d8812ec7e1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!wkci!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F266da9d4-bcf5-44fc-a059-b3d8812ec7e1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!wkci!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F266da9d4-bcf5-44fc-a059-b3d8812ec7e1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!wkci!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F266da9d4-bcf5-44fc-a059-b3d8812ec7e1_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A role test is a small idea with a large design consequence.</p><p>It says that an abstraction means something.</p><p>An interface tells the compiler which messages an object accepts. It does not fully describe what clients may assume after sending those messages.</p><p>That is how an in-memory repository and a PostgreSQL repository can both implement:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;6cbf9e35-e77a-40e7-ba55-4f15589fd86e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">fun save(customer: Customer)</code></pre></div><p>while disagreeing about duplicate identity.</p><p>A role test turns the client-visible promises of that abstraction into shared executable examples.</p><p>It does not ask the fake to copy PostgreSQL. It does not ask the new dependency to reproduce every legacy accident. It does not require alternative strategies to produce identical results when their purposes differ.</p><p>In the object-oriented terminology documented by Rivero and Gorman, this is contract testing applied polymorphically: one contract suite is executed against every implementation that claims the role. The name <em>role test</em> makes the intended meaning explicit and avoids confusing it with consumer-provider contract testing across distributed boundaries.</p><p>It asks each implementation:</p><blockquote><p>Do you satisfy the behaviour promised by this role?</p></blockquote><p>That question helps in daily design:</p><ul><li><p>A fake and a real adapter remain aligned on shared semantics</p></li><li><p>Several production adapters can be checked against one role</p></li><li><p>A new Strategy can preserve required business rules</p></li><li><p>An old and a new dependency can coexist behind one abstraction during a gradual refactor</p></li><li><p>Disagreements reveal missing contract decisions</p></li></ul><p>The technique has limits. A passing suite covers only the properties it expresses. Implementation-specific, integration, distributed-contract, performance, security, and operational tests remain necessary.</p><p>But the shared suite gives the team something an interface alone cannot provide: an executable statement of meaning.</p><p>The interface gives the collaboration a vocabulary.</p><p>The role test gives that vocabulary behavioural meaning.</p><h2>References</h2><ul><li><p>Aniche, Maur&#237;cio. <em>Effective Software Testing: A Developer&#8217;s Guide</em>. <a href="https://www.manning.com/books/effective-software-testing">https://www.manning.com/books/effective-software-testing</a></p></li><li><p>Cruz, Marie; Glover, Lewis; Mittal, Madhura; Prescott, Lewis. <em>Contract Testing in Action</em>. <a href="https://www.manning.com/books/contract-testing-in-action">https://www.manning.com/books/contract-testing-in-action</a></p></li><li><p>Feathers, Michael C. <em>Working Effectively with Legacy Code</em>. <a href="https://www.pearson.com/en-us/subject-catalog/p/working-effectively-with-legacy-code/P200000000231">https://www.pearson.com/en-us/subject-catalog/p/working-effectively-with-legacy-code/P200000000231</a></p></li><li><p>Fowler, Martin. <em>Branch by Abstraction</em>. <a href="https://martinfowler.com/bliki/BranchByAbstraction.html">https://martinfowler.com/bliki/BranchByAbstraction.html</a></p></li><li><p>Fowler, Martin. <em>Contract Test</em>. <a href="https://martinfowler.com/bliki/ContractTest.html">https://martinfowler.com/bliki/ContractTest.html</a></p></li><li><p>Fowler, Martin. <em>Mocks Aren&#8217;t Stubs</em>. <a href="https://martinfowler.com/articles/mocksArentStubs.html">https://martinfowler.com/articles/mocksArentStubs.html</a></p></li><li><p>Freeman, Steve; Mackinnon, Tim; Pryce, Nat; Walnes, Joe. <em>Mock Roles, not Objects</em>. <a href="https://jmock.org/oopsla2004.pdf">https://jmock.org/oopsla2004.pdf</a></p></li><li><p>Freeman, Steve; Pryce, Nat. <em>Growing Object-Oriented Software, Guided by Tests</em>. </p><p><a href="https://www.growing-object-oriented-software.com/">https://www.growing-object-oriented-software.com/</a></p></li><li><p>Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John. <em>Design Patterns: Elements of Reusable Object-Oriented Software</em>. <a href="https://www.pearson.com/en-us/subject-catalog/p/design-patterns-elements-of-reusable-object-oriented-software/P200000009480">https://www.pearson.com/en-us/subject-catalog/p/design-patterns-elements-of-reusable-object-oriented-software/P200000009480</a></p></li><li><p>Gorman, Jason. <em>ContractTesting: Simple example of polymorphic/contract testing in JUnit</em>. <a href="https://github.com/jasongorman/ContractTesting">https://github.com/jasongorman/ContractTesting</a></p></li><li><p>JUnit Team. <em>Test Interfaces and Default Methods</em>. <a href="https://docs.junit.org/6.1.0/writing-tests/test-interfaces-and-default-methods.html">https://docs.junit.org/6.1.0/writing-tests/test-interfaces-and-default-methods.html</a></p></li><li><p>Liskov, Barbara; Wing, Jeannette. <em>A Behavioral Notion of Subtyping</em>. <a href="https://www.cs.cmu.edu/~wing/publications/LiskovWing94.pdf">https://www.cs.cmu.edu/~wing/publications/LiskovWing94.pdf</a></p></li><li><p>Meszaros, Gerard. <em>xUnit Test Patterns: Refactoring Test Code</em>. </p><p><a href="http://xunitpatterns.com/">http://xunitpatterns.com/</a></p></li><li><p>Meyer, Bertrand. <em>Building Bug-Free O-O Software: An Introduction to Design by Contract</em>. <a href="https://www.eiffel.com/values/design-by-contract/introduction/">https://www.eiffel.com/values/design-by-contract/introduction/</a></p></li><li><p>OpenAPI Initiative. <em>OpenAPI Specification</em>. <a href="https://spec.openapis.org/oas/latest.html">https://spec.openapis.org/oas/latest.html</a></p></li><li><p>Rainsberger, J. B. <em>Integrated Tests Are a Scam</em>. </p></li></ul><div id="vimeo-80533536" class="vimeo-wrap" data-attrs="{&quot;videoId&quot;:&quot;80533536&quot;,&quot;videoKey&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="VimeoToDOM"><div class="vimeo-inner"><iframe src="https://player.vimeo.com/video/80533536?autoplay=0" frameborder="0" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" loading="lazy"></iframe></div></div><ul><li><p>Rivero, Manuel. <em>Example of role tests in Java with JUnit</em>. <a href="https://codesai.com/posts/2022/08/role-tests-junit">https://codesai.com/posts/2022/08/role-tests-junit</a></p></li><li><p>Rivero, Manuel. <em>Role tests for implementation of interfaces discovered through TDD</em>. <a href="https://codesai.com/posts/2022/04/role-tests">https://codesai.com/posts/2022/04/role-tests</a></p></li><li><p>Sato, Danilo. <em>Parallel Change</em>. <a href="https://martinfowler.com/bliki/ParallelChange.html">https://martinfowler.com/bliki/ParallelChange.html</a></p></li><li><p>Wirfs-Brock, Rebecca; McKean, Alan. <em>Object Design: Roles, Responsibilities, and Collaborations</em>. <a href="https://www.informit.com/store/object-design-roles-responsibilities-and-collaborations-9780201379433">https://www.informit.com/store/object-design-roles-responsibilities-and-collaborations-9780201379433</a></p></li><li><p>Wirfs-Brock, Rebecca; Wilkerson, Brian; Wiener, Lauren. <em>Designing Object-Oriented Software</em>. <a href="https://dl.acm.org/doi/book/10.5555/573097">https://dl.acm.org/doi/book/10.5555/573097</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Agents are not a new abstraction layer. They are a new place to hide the bill]]></title><description><![CDATA[LLM agentic development can be useful, but calling it an abstraction layer is how teams rename verification cost as progress.]]></description><link>https://emmanuelvalverderamos.substack.com/p/agents-are-not-a-new-abstraction</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/agents-are-not-a-new-abstraction</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Mon, 29 Jun 2026 13:00:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!8f5O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1745931-c57d-425f-985c-309e62cd8bc7_1024x559.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Disclaimer</h2><p>This article is a direct response to Sebastian Bergmann&#8217;s article <em>Beyond Best Practices</em>, published on phpunit.expert.</p><p>Sebastian Bergmann is the creator of PHPUnit, and his article makes a serious argument: LLM-based coding agents are not replacing compilers, but extending the historical progression of software abstraction. In that framing, we moved from machine code to assembly, then to higher-level languages, frameworks, libraries, and now agentic systems. The claim is that agents let us focus on what software should do and why, while the implementation details move into automation.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MtPu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14dd96a-a2ef-4f81-8d60-52fd05310ebd_1237x1794.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MtPu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14dd96a-a2ef-4f81-8d60-52fd05310ebd_1237x1794.png 424w, https://substackcdn.com/image/fetch/$s_!MtPu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14dd96a-a2ef-4f81-8d60-52fd05310ebd_1237x1794.png 848w, https://substackcdn.com/image/fetch/$s_!MtPu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14dd96a-a2ef-4f81-8d60-52fd05310ebd_1237x1794.png 1272w, https://substackcdn.com/image/fetch/$s_!MtPu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14dd96a-a2ef-4f81-8d60-52fd05310ebd_1237x1794.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MtPu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14dd96a-a2ef-4f81-8d60-52fd05310ebd_1237x1794.png" width="1237" height="1794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c14dd96a-a2ef-4f81-8d60-52fd05310ebd_1237x1794.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1794,&quot;width&quot;:1237,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:607467,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84085623-ccf0-4910-91fc-c02f745f903b_1365x1794.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_!MtPu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14dd96a-a2ef-4f81-8d60-52fd05310ebd_1237x1794.png 424w, https://substackcdn.com/image/fetch/$s_!MtPu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14dd96a-a2ef-4f81-8d60-52fd05310ebd_1237x1794.png 848w, https://substackcdn.com/image/fetch/$s_!MtPu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14dd96a-a2ef-4f81-8d60-52fd05310ebd_1237x1794.png 1272w, https://substackcdn.com/image/fetch/$s_!MtPu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14dd96a-a2ef-4f81-8d60-52fd05310ebd_1237x1794.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I respect the seriousness of the argument. I also think this specific abstraction claim is wrong.</p><p>Not because agents are useless.</p><p>Not because automation is bad.</p><p>Not because tests, documentation, code review, static analysis, and architectural constraints are unimportant. They are essential.</p><p>The problem is more basic: comparing LLM agentic development with abstraction only works if we weaken the meaning of abstraction until it becomes almost indistinguishable from hiding, delegation, or automation.</p><p>That is the mistake this article rejects.</p><p>A real abstraction lets us ignore selected details because the model underneath has stable semantics, reliable boundaries, and a contract strong enough to reason with. If the thing forces me to know its hidden behavior, model quirks, prompt sensitivity, tool limitations, and failure patterns in order to use it safely, then it has not raised the level of abstraction in the engineering sense. It has moved the cost somewhere else.</p><p>At best, that is a leaky abstraction.</p><p>At worst, it is not an abstraction at all.</p><p>It is just another tool with a nicer story.</p><p>Imagine taking your car to a workshop because the engine makes a strange noise. The mechanic disappears behind a curtain, returns with a confident smile, and tells you the car is fixed.</p><p>Then he hands you a flashlight, a torque wrench, a diagnostic scanner, and a warning:</p><div class="callout-block" data-callout="true"><p>&#8220;Before driving, please check every bolt, every cable, every fluid level, and every safety-critical part. Sometimes I improvise.&#8221;</p></div><p>That is not an abstraction over car repair.</p><p>That is delegation without trust.</p><p>The work may still be useful. The first pass may save time. The mechanic may have found something you missed. But if the final responsibility, the verification burden, the diagnosis of mistakes, and the cost of rework all remain yours, the curtain did not become an abstraction layer. It became a place where risk disappears for a moment before it returns wearing a cleaner shirt.</p><p>This is where much of the current conversation around <em>Large Language Model</em> (LLM) agentic development goes wrong. We are told that agents are the next layer in the long history of software abstraction. Assembly gave way to C. C gave way to higher-level languages. Operating systems hid hardware. Cloud platforms hid machines. Kubernetes hid servers behind desired state. Now, supposedly, agents will hide programming itself behind intent.</p><p>That story sounds elegant. It is also dangerously lazy.</p><p>LLM agentic development is not automatically a new abstraction layer. It can be a useful tool. It can be a powerful accelerator in some contexts. It can help explore, scaffold, transform, summarize, and automate slices of work. But calling it a new abstraction layer without asking what contract it provides, what semantics it preserves, what failures it bounds, what costs it moves, and what evidence supports it is bullshit.</p><p>The problem is not that teams are experimenting with LLM agents. The problem is that many are trying to convince themselves that moving uncertainty behind a prompt, a model, an agent runner, and a tool-calling loop is the same thing as raising the level of abstraction.</p><p>It is not.</p><h2>TL;DR</h2><p>LLM agentic development is better understood as probabilistic orchestration with delegated execution and delayed verification than as a software abstraction layer.</p><p>A real abstraction gives us a usable model, stable boundaries, preserved semantics, and a way to reduce the number of details we must track for a given purpose. It does not make reality disappear. It decides which details are irrelevant at a particular level and gives us a contract strong enough to work safely without constantly opening the box.</p><p>Current LLM agents do not provide that kind of contract for software development as a whole. They can plan, call tools, edit files, run commands, inspect failures, and produce plausible outputs. Their behavior still depends on model versions, prompts, context selection, tool availability, memory, permissions, infrastructure, and human supervision.</p><p>That does not make them useless. It makes them something different.</p><p>They are useful when the task is bounded, the feedback loop is fast, the cost of error is acceptable, and verification is independent. They become dangerous when organizations treat their fluency as correctness, their speed as delivery, or their autonomy as abstraction.</p><p>Usefulness is not the same as abstraction.</p><p>Speed is not the same as delivery.</p><p>Fluency is not the same as correctness.</p><p>The honest claim is this: LLM agents can reduce some local work while adding new verification, review, security, governance, and maintenance work elsewhere. Whether that is worth it depends on context, measurement, and the cost of keeping the system trustworthy.</p><p>The dishonest claim is this: agentic development is simply the next abstraction layer, so skepticism is just nostalgia.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Table of contents</h2><ul><li><p><a href="?open=false#&#167;a-curtain-is-not-an-abstraction">A curtain is not an abstraction</a></p></li><li><p><a href="?open=false#&#167;what-abstraction-actually-means">What abstraction actually means</a></p></li><li><p><a href="?open=false#&#167;abstraction-in-software-is-a-contract-not-a-vibe">Abstraction in software is a contract, not a vibe</a></p></li><li><p><a href="?open=false#&#167;abstraction-is-not-hiding-automation-delegation-orchestration-or-compression">Abstraction is not hiding, automation, delegation, orchestration, or compression</a></p></li><li><p><a href="?open=false#&#167;if-i-must-know-the-models-quirks-what-exactly-was-abstracted">If I must know the model&#8217;s quirks, what exactly was abstracted?</a></p></li><li><p><a href="?open=false#&#167;why-the-old-abstraction-ladder-is-seductive">Why the old abstraction ladder is seductive</a></p></li><li><p><a href="?open=false#&#167;what-llm-agentic-development-actually-is">What LLM agentic development actually is</a></p></li><li><p><a href="?open=false#&#167;the-claim-i-am-rejecting">The claim I am rejecting</a></p></li><li><p><a href="?open=false#&#167;the-missing-contract">The missing contract</a></p></li><li><p><a href="?open=false#&#167;non-determinism-is-not-a-footnote">Non-determinism is not a footnote</a></p></li><li><p><a href="?open=false#&#167;hallucination-is-not-a-leaky-abstraction">Hallucination is not a leaky abstraction</a></p></li><li><p><a href="?open=false#&#167;the-infrastructure-matters-more-than-the-slogan">The infrastructure matters more than the slogan</a></p></li><li><p><a href="?open=false#&#167;the-hidden-detail-is-not-the-problem-hidden-responsibility-is">The hidden detail is not the problem; hidden responsibility is</a></p></li><li><p><a href="?open=false#&#167;we-already-learned-this-lesson-with-frameworks">We already learned this lesson with frameworks</a></p></li><li><p><a href="?open=false#&#167;the-evidence-is-mixed-and-that-matters">The evidence is mixed, and that matters</a></p></li><li><p><a href="?open=false#&#167;what-to-measure-before-believing-the-story">What to measure before believing the story</a></p></li><li><p><a href="?open=false#&#167;where-llm-agents-are-genuinely-useful">Where LLM agents are genuinely useful</a></p></li><li><p><a href="?open=false#&#167;where-the-abstraction-claim-collapses">Where the abstraction claim collapses</a></p></li><li><p><a href="?open=false#&#167;a-test-for-calling-something-an-abstraction-layer">A test for calling something an abstraction layer</a></p></li><li><p><a href="?open=false#&#167;a-better-mental-model">A better mental model</a></p></li><li><p><a href="?open=false#&#167;the-sharp-line">The sharp line</a></p></li><li><p><a href="?open=false#&#167;conclusion">Conclusion</a></p></li><li><p><a href="?open=false#&#167;references">References</a></p></li></ul><h2>A curtain is not an abstraction</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hSCI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fadf8b8-1899-4ed3-9e51-d301d930ee30_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hSCI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fadf8b8-1899-4ed3-9e51-d301d930ee30_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!hSCI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fadf8b8-1899-4ed3-9e51-d301d930ee30_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!hSCI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fadf8b8-1899-4ed3-9e51-d301d930ee30_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!hSCI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fadf8b8-1899-4ed3-9e51-d301d930ee30_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hSCI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fadf8b8-1899-4ed3-9e51-d301d930ee30_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8fadf8b8-1899-4ed3-9e51-d301d930ee30_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:667830,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fadf8b8-1899-4ed3-9e51-d301d930ee30_1024x559.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_!hSCI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fadf8b8-1899-4ed3-9e51-d301d930ee30_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!hSCI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fadf8b8-1899-4ed3-9e51-d301d930ee30_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!hSCI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fadf8b8-1899-4ed3-9e51-d301d930ee30_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!hSCI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fadf8b8-1899-4ed3-9e51-d301d930ee30_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A curtain hides. An abstraction explains by choosing what not to show.</p><p>That distinction matters. A curtain can cover a messy kitchen. It can also cover a fire. Hiding detail is only useful when the hidden detail is either irrelevant to the current task or controlled by a reliable boundary.</p><p>A car steering wheel is an abstraction because, for normal driving, you do not need to understand every mechanical detail between the wheel and the tyres. The abstraction has properties. Turning left has a stable meaning. Resistance in the wheel gives meaningful feedback. The driver has a usable model, and that model is reliable enough for the purpose.</p><p>A restaurant menu is also an abstraction. You order &#8220;mushroom risotto,&#8221; not &#8220;ninety grams of arborio rice, stock added gradually, stirred until starch release gives a creamy texture.&#8221; But the menu only works because there is a kitchen, a recipe, a supply chain, a cook, and a shared cultural expectation of what &#8220;risotto&#8221; means.</p><p>If the kitchen sometimes returns soup, sometimes rice pudding, and sometimes a philosophical essay about mushrooms, the menu is no longer a useful abstraction. It is a gamble with typography.</p><p>Software abstraction works the same way. It is not a decorative layer of words. It is a model with boundaries, responsibilities, and consequences.</p><p>When someone says &#8220;LLM agents are just another abstraction layer,&#8221; the first question should be simple:</p><p>What exactly can I stop knowing, stop checking, or stop controlling because this layer exists?</p><p>If the honest answer is &#8220;nothing important,&#8221; then we are not discussing abstraction. We are discussing a tool that still requires supervision.</p><p>That tool may still be valuable. A supervised tool can be valuable. A powerful assistant can be valuable. A fast generator can be valuable. But value is not the same as abstraction.</p><p>A curtain hides the mess. An abstraction changes how we can safely think.</p><h2>What abstraction actually means</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!docH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a6968d7-2482-4af2-91bf-88201bd9199b_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!docH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a6968d7-2482-4af2-91bf-88201bd9199b_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!docH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a6968d7-2482-4af2-91bf-88201bd9199b_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!docH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a6968d7-2482-4af2-91bf-88201bd9199b_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!docH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a6968d7-2482-4af2-91bf-88201bd9199b_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!docH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a6968d7-2482-4af2-91bf-88201bd9199b_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7a6968d7-2482-4af2-91bf-88201bd9199b_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:956287,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a6968d7-2482-4af2-91bf-88201bd9199b_1024x559.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_!docH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a6968d7-2482-4af2-91bf-88201bd9199b_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!docH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a6968d7-2482-4af2-91bf-88201bd9199b_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!docH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a6968d7-2482-4af2-91bf-88201bd9199b_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!docH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a6968d7-2482-4af2-91bf-88201bd9199b_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Abstraction is the act of representing something by deliberately leaving out details that are not relevant to a particular purpose while preserving the properties that are relevant.</p><p>That last part carries the weight: preserving the properties that are relevant.</p><p>A map is an abstraction of territory. A subway map may distort distance, geography, and scale, but it preserves connectivity. That is why it works. It would be a poor map for hiking and a useful map for changing trains. Its value depends on purpose.</p><p>A calendar is an abstraction of time. It ignores weather, mood, energy, uncertainty, and human exhaustion, but it preserves dates, durations, recurrence, and sequence. It is useful for planning a meeting. It is not enough to understand whether a team has cognitive capacity to absorb another project.</p><p>An invoice is an abstraction of economic exchange. It does not include every conversation, every trade-off, every frustration, or every late-night decision. It preserves parties, amounts, dates, obligations, and payment state. That is why it can move through accounting systems.</p><p>Good abstractions are not vague. They are selective. They have a point of view.</p><p>A useful abstraction does not merely hide detail. It changes the flow of knowledge. It removes knowledge the caller should not need, while preserving the knowledge the caller must have to act safely. If it exposes irrelevant detail, it creates noise. If it hides essential detail, it creates risk.</p><p>In computing, abstraction is not a luxury. It is how we survive scale. We cannot reason about every transistor, scheduler decision, cache line, system call, network packet, object, database page, dependency, and user behavior at the same time. We build models that allow us to think at a chosen level.</p><p>Procedures, types, modules, protocols, relational tables, objects, queues, containers, virtual machines, and cloud resources are all ways of drawing a boundary around complexity.</p><p>But every useful boundary also makes a promise.</p><p>It says: &#8220;For this purpose, you can think about me like this.&#8221;</p><p>The moment that promise is weak, unstable, or unverifiable, the abstraction begins to rot.</p><h2>Abstraction in software is a contract, not a vibe</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!k2_g!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de7c62-df0d-4003-856c-0a513a25f066_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!k2_g!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de7c62-df0d-4003-856c-0a513a25f066_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!k2_g!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de7c62-df0d-4003-856c-0a513a25f066_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!k2_g!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de7c62-df0d-4003-856c-0a513a25f066_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!k2_g!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de7c62-df0d-4003-856c-0a513a25f066_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!k2_g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de7c62-df0d-4003-856c-0a513a25f066_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a4de7c62-df0d-4003-856c-0a513a25f066_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:932277,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de7c62-df0d-4003-856c-0a513a25f066_1024x559.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_!k2_g!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de7c62-df0d-4003-856c-0a513a25f066_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!k2_g!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de7c62-df0d-4003-856c-0a513a25f066_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!k2_g!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de7c62-df0d-4003-856c-0a513a25f066_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!k2_g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4de7c62-df0d-4003-856c-0a513a25f066_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A software abstraction is not just a nicer interface. It is a contract between the user of the abstraction and the thing being abstracted.</p><p>That contract may be formal, like a type signature, protocol specification, schema, or database constraint. It may be semi-formal, like an architectural boundary, module interface, or documented behavior. It may be cultural, like a team convention backed by tests, review discipline, and shared ownership.</p><p>But it must exist.</p><p>Without a contract, the abstraction becomes a rumor.</p><p>A useful software abstraction usually gives us several things:</p><ol><li><p>A stable interface</p></li><li><p>Preserved semantics</p></li><li><p>Hidden implementation details</p></li><li><p>Explicit failure modes</p></li><li><p>Composability</p></li><li><p>Observability at the boundary</p></li><li><p>Accountability<br>This is why David Parnas matters here. His work on modular decomposition was not about placing code into separate files. It was about using information hiding as a design criterion. A good module hides a design decision likely to change, so the rest of the system does not become coupled to that decision.</p></li></ol><p>That is abstraction with teeth.</p><p>Barbara Liskov&#8217;s work on data abstraction points in the same direction. Data abstraction helps organize programs so they can be modified and maintained. Again, the important move is not &#8220;hide something somewhere.&#8221; The important move is to define behavior through an abstraction that users can rely on while implementations remain replaceable.</p><p>A stack is a simple example. The user of a stack should not need to know whether it is backed by an array, a linked structure, contiguous memory, a persistent data structure, or something else. But the user does need stable semantics for push, pop, empty state, and errors.</p><p>If <code>pop</code> sometimes returns the last item, sometimes invents an item, and sometimes rewrites the caller&#8217;s source code, we would not celebrate it as a bold new abstraction.</p><p>We would call it broken.</p><h2>Abstraction is not hiding, automation, delegation, orchestration, or compression</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8f5O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1745931-c57d-425f-985c-309e62cd8bc7_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8f5O!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1745931-c57d-425f-985c-309e62cd8bc7_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!8f5O!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1745931-c57d-425f-985c-309e62cd8bc7_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!8f5O!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1745931-c57d-425f-985c-309e62cd8bc7_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!8f5O!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1745931-c57d-425f-985c-309e62cd8bc7_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8f5O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1745931-c57d-425f-985c-309e62cd8bc7_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c1745931-c57d-425f-985c-309e62cd8bc7_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:806682,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1745931-c57d-425f-985c-309e62cd8bc7_1024x559.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_!8f5O!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1745931-c57d-425f-985c-309e62cd8bc7_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!8f5O!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1745931-c57d-425f-985c-309e62cd8bc7_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!8f5O!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1745931-c57d-425f-985c-309e62cd8bc7_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!8f5O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1745931-c57d-425f-985c-309e62cd8bc7_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The agentic development conversation often collapses several different ideas into one glamorous word: abstraction.</p><p>That collapse is the source of much of the confusion.</p><h3>Abstraction is not hiding</h3><p>Hiding is only part of abstraction. Hiding becomes useful when the hidden detail is irrelevant at the current level or controlled by a reliable contract.</p><p>A black box is not automatically an abstraction. A black box can also be a liability.</p><p>When an agent hides planning, context selection, tool execution, and code generation behind a chat interface, it has hidden activity. It has not necessarily created a safe model for reasoning.</p><p>The question is not &#8220;did it hide detail?&#8221;</p><p>The question is &#8220;did it preserve the right meaning while hiding the right detail?&#8221;</p><h3>Abstraction is not automation</h3><p>Automation performs work. Abstraction changes the model we reason with.</p><p>A script that formats files is automation. It may save time, but it does not necessarily change the conceptual model of the system. A compiler is both automation and abstraction. It automates translation and gives programmers a higher-level language with stable semantics.</p><p>An LLM agent may automate parts of software work. It may generate boilerplate, run tests, search documentation, produce migration drafts, or propose refactorings. That can be useful. But automating fragments of work does not automatically create a new abstraction layer over software development.</p><p>Automation asks: &#8220;Can the machine perform this task?&#8221;</p><p>Abstraction asks: &#8220;Can I safely reason at another level?&#8221;</p><p>Those are different questions.</p><h3>Abstraction is not delegation</h3><p>Delegation gives work to another actor. Abstraction lets us ignore selected implementation details because the boundary is reliable enough.</p><p>A junior developer can implement a task. That does not make the junior developer an abstraction layer over software engineering. A contractor can write code. A consultant can propose architecture. A team can outsource a subsystem. Delegation moves work, but it does not erase responsibility.</p><p>LLM agents often create delegation without ownership. The human delegates a task, receives an output, and remains responsible for correctness, maintainability, security, and integration.</p><p>That is not abstraction.</p><p>That is supervised delegation.</p><h3>Abstraction is not orchestration</h3><p>Orchestration coordinates steps, tools, services, and feedback loops.</p><p>An agentic system may inspect files, call tools, run tests, edit code, evaluate failures, and try again. That is orchestration. It may be powerful. It may be impressive. It may produce useful results.</p><p>But orchestration is not automatically abstraction.</p><p>A chaotic process can be orchestrated. A fragile deployment pipeline can be orchestrated. A sequence of unreliable actions can be orchestrated. Coordination does not imply a stable conceptual model.</p><p>Agentic development is often better described as probabilistic orchestration with delegated execution and delayed verification than as abstraction.</p><p>That description is less glamorous, but more accurate.</p><h3>Abstraction is not compression</h3><p>LLMs are good at compression in the human sense. They can summarize code, explain logs, condense documentation, and turn large context into fluent language.</p><p>That can help. A summary can orient a developer. A generated explanation can reduce the cost of entering a codebase. A condensed plan can make a task feel manageable.</p><p>But compression can lose meaning.</p><p>A summary is not a contract. A fluent explanation is not evidence. A confident plan is not correctness. A compressed version of a codebase may omit the very detail that matters.</p><p>In software, the dangerous detail is often not loud. It is quiet. It lives in edge cases, production incidents, implicit invariants, old migrations, strange customer behavior, and comments nobody trusts but nobody deletes.</p><p>A model that compresses context can help us navigate complexity. It does not automatically abstract it.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;4fa39840-fd64-4cf3-9dd8-59c4d6c03cc2&quot;,&quot;caption&quot;:&quot;Imagine downloading a framework that promises to remove half your work.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Why LLM agentic development is the worst developer experience&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-05-07T05:22:47.335Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!nJKu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f5d484-33dd-4c12-9bf7-e541a4026de8_1024x559.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/why-llm-agentic-development-is-the&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:194764745,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>If I must know the model&#8217;s quirks, what exactly was abstracted?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-QFz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa65d01e5-b992-448d-a6e3-db4632656eb4_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-QFz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa65d01e5-b992-448d-a6e3-db4632656eb4_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!-QFz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa65d01e5-b992-448d-a6e3-db4632656eb4_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!-QFz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa65d01e5-b992-448d-a6e3-db4632656eb4_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!-QFz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa65d01e5-b992-448d-a6e3-db4632656eb4_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-QFz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa65d01e5-b992-448d-a6e3-db4632656eb4_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a65d01e5-b992-448d-a6e3-db4632656eb4_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:799205,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa65d01e5-b992-448d-a6e3-db4632656eb4_1024x559.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_!-QFz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa65d01e5-b992-448d-a6e3-db4632656eb4_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!-QFz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa65d01e5-b992-448d-a6e3-db4632656eb4_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!-QFz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa65d01e5-b992-448d-a6e3-db4632656eb4_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!-QFz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa65d01e5-b992-448d-a6e3-db4632656eb4_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A simple example is enough.</p><p>Suppose I ask a model to generate a Mermaid diagram with labels that need line breaks. One model may correctly use <code>&lt;br&gt;</code> because Mermaid expects HTML-style line breaks in many label contexts. Another, weaker or differently trained model may use <code>\n</code>, because it confuses a textual newline escape with Mermaid&#8217;s rendering rules.</p><p>The result is not just a cosmetic issue. The moment I need to know which model handles Mermaid line breaks correctly, which one confuses <code>&lt;br&gt;</code> with <code>\n</code>, and how to defend myself against that difference, I am no longer reasoning at a clean higher level.</p><p>I am reasoning about the implementation behavior of the supposed abstraction.</p><p>That is exactly the kind of thing software engineers normally call a leaky abstraction. The leak is not that something went wrong once. The leak is that safe use requires knowledge of what was supposedly hidden.</p><p>And this gets worse when teams want to treat models as interchangeable.</p><p>The industry loves saying &#8220;use the model that fits the task,&#8221; as if swapping one LLM for another were like swapping one implementation behind a stable interface. But that is often not true. Different models have different failure modes, different instruction-following behavior, different context sensitivity, different tool-use reliability, different formatting quirks, different hallucination profiles, and different behavior under pressure.</p><p>If replacing the model changes the behavior of the system in ways the caller must understand, then the model boundary is not acting like a reliable abstraction.</p><p>It is acting like a dependency.</p><p>And dependencies need management.</p><h2>Why the old abstraction ladder is seductive</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QqoD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc532c7-296f-4b6f-9fc0-d19dc91515c0_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QqoD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc532c7-296f-4b6f-9fc0-d19dc91515c0_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!QqoD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc532c7-296f-4b6f-9fc0-d19dc91515c0_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!QqoD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc532c7-296f-4b6f-9fc0-d19dc91515c0_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!QqoD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc532c7-296f-4b6f-9fc0-d19dc91515c0_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QqoD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc532c7-296f-4b6f-9fc0-d19dc91515c0_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4fc532c7-296f-4b6f-9fc0-d19dc91515c0_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:486543,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc532c7-296f-4b6f-9fc0-d19dc91515c0_1024x559.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_!QqoD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc532c7-296f-4b6f-9fc0-d19dc91515c0_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!QqoD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc532c7-296f-4b6f-9fc0-d19dc91515c0_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!QqoD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc532c7-296f-4b6f-9fc0-d19dc91515c0_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!QqoD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc532c7-296f-4b6f-9fc0-d19dc91515c0_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The industry loves a ladder.</p><p>Machine code. Assembly. C. Managed runtimes. Frameworks. Cloud. Serverless. Agents.</p><p>The ladder feels obvious because software history really does contain successful abstraction jumps. High-level languages removed much of the need to think in registers. Operating systems gave programs a model of files, processes, memory, and devices. Databases let us ask for sets of data without hand-writing storage traversal. Cloud platforms let teams provision capabilities through APIs instead of buying hardware.</p><p>So the story writes itself: LLM agents are the next rung. We will stop writing code and start expressing intent. We will stop managing implementation and start managing outcomes. Programming will move from syntax to goals.</p><p>There is a grain of truth there. Programming has always moved between levels of description. We do build more powerful notations. We do create better tools. We do automate old manual work. We do convert repeated expert activity into reusable mechanisms.</p><p>But the old abstraction story leaves out the price of admission.</p><p>C did not become useful because it was more English-like than assembly. It became useful because compilers, linkers, runtimes, conventions, debuggers, operating systems, and hardware targets formed a technical ecosystem with stable enough semantics.</p><p><em>Structured Query Language</em> (SQL) did not become useful because it magically removed data complexity. It became useful because relational theory, database engines, query optimizers, transactions, indexes, constraints, and execution plans created a practical contract. The abstraction leaks, especially through performance, but the model is not arbitrary.</p><p>Kubernetes did not become useful because YAML is beautiful. It is not. It became useful because desired state, controllers, reconciliation loops, scheduling, health checks, service discovery, and declarative APIs created a model teams could operate, debug, and automate. The abstraction leaks often, but it has a shape.</p><p>The history of abstraction is not the history of hiding more.</p><p>It is the history of inventing stronger models.</p><p>That is the part the agentic development hype usually skips.</p><h2>What LLM agentic development actually is</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4gsK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc92d3e4-ed66-4755-9384-93da5da15b72_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4gsK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc92d3e4-ed66-4755-9384-93da5da15b72_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!4gsK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc92d3e4-ed66-4755-9384-93da5da15b72_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!4gsK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc92d3e4-ed66-4755-9384-93da5da15b72_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!4gsK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc92d3e4-ed66-4755-9384-93da5da15b72_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4gsK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc92d3e4-ed66-4755-9384-93da5da15b72_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc92d3e4-ed66-4755-9384-93da5da15b72_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:838413,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc92d3e4-ed66-4755-9384-93da5da15b72_1024x559.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_!4gsK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc92d3e4-ed66-4755-9384-93da5da15b72_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!4gsK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc92d3e4-ed66-4755-9384-93da5da15b72_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!4gsK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc92d3e4-ed66-4755-9384-93da5da15b72_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!4gsK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc92d3e4-ed66-4755-9384-93da5da15b72_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>LLM agentic development is a socio-technical loop where an LLM is given instructions, context, tools, permissions, and feedback so it can perform multi-step software work.</p><p>In the simplest case, the model suggests code. In a more agentic case, the system can inspect files, plan changes, call tools, run commands, execute tests, search documentation, edit code, and iterate.</p><p>Some systems follow predefined workflows. Others let the model dynamically choose its own process and tool usage. Anthropic&#8217;s guidance makes this distinction explicit: workflows are systems where LLMs and tools are orchestrated through predefined code paths, while agents direct their own processes and tool use while working toward a goal.</p><p>That distinction matters.</p><p>A workflow is code-driven orchestration with LLM calls inside it. An agent gives the LLM more control over process and action selection. The more control the model gets, the more the system depends on the model&#8217;s judgment, the quality of context, the safety of tools, the permission model, and the strength of verification.</p><p>A simplified agentic development loop looks like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xyy3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa75d729-cc6c-4036-b29d-6b13d84f315a_717x1900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xyy3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa75d729-cc6c-4036-b29d-6b13d84f315a_717x1900.png 424w, https://substackcdn.com/image/fetch/$s_!xyy3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa75d729-cc6c-4036-b29d-6b13d84f315a_717x1900.png 848w, https://substackcdn.com/image/fetch/$s_!xyy3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa75d729-cc6c-4036-b29d-6b13d84f315a_717x1900.png 1272w, https://substackcdn.com/image/fetch/$s_!xyy3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa75d729-cc6c-4036-b29d-6b13d84f315a_717x1900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xyy3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa75d729-cc6c-4036-b29d-6b13d84f315a_717x1900.png" width="717" height="1900" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fa75d729-cc6c-4036-b29d-6b13d84f315a_717x1900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1900,&quot;width&quot;:717,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:96081,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa75d729-cc6c-4036-b29d-6b13d84f315a_717x1900.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_!xyy3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa75d729-cc6c-4036-b29d-6b13d84f315a_717x1900.png 424w, https://substackcdn.com/image/fetch/$s_!xyy3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa75d729-cc6c-4036-b29d-6b13d84f315a_717x1900.png 848w, https://substackcdn.com/image/fetch/$s_!xyy3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa75d729-cc6c-4036-b29d-6b13d84f315a_717x1900.png 1272w, https://substackcdn.com/image/fetch/$s_!xyy3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa75d729-cc6c-4036-b29d-6b13d84f315a_717x1900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><a href="https://mermaid.live/edit#pako:eNpNkstOxDAMRX8lyros2HbBZkYCJEZCMGJBy8Ikbhs1jSvHZUCIfyeP4bFp4-Rc-_Y2n9qQRd3qwdPJTMCijvs-KHUbBIN0N9sCQblSvKiLiyt1z7Ss0tWXgmCVQJzVwLC4ML5k7fks0ztK0nfpHtGjEbSKcaXohPhDmXpWJGeuaA7Jke_u7g5p7oCMwWBhyn714CF01xiQIfdcU1kHp0UBjkQ-dvmpDHgfi8_BeVRoncQCF6a2AzFTt0vtGyUYJTbZ2-DGRlkylS5MoZ-Q3eAMiKNwzofxzeGpDIFNaCmuzIRmruL_ktLjAU_E8znEFFseyJwSSkCjmLx_BTOnFVqMbqxfV0V_EelGj-ysboU3bPSCvEAu9Wemey0TLtjrNi0tDrB56XUfvpJshfBMtPwombZx0u0APqZqW23yv3cwpl_6i2CwyDvaguj2stE5RuJDvTvlCn19AzMuy8Q">Open and edit this diagram in Mermaid Live</a></figcaption></figure></div><p>This is not one layer. It is a loop made of several moving parts:</p><ol><li><p>The base model</p></li><li><p>The prompt</p></li><li><p>The context window</p></li><li><p>The retrieval mechanism</p></li><li><p>The repository state</p></li><li><p>The tools exposed to the agent</p></li><li><p>The permissions granted to those tools</p></li><li><p>The execution environment</p></li><li><p>The tests and checks available</p></li><li><p>The human reviewer</p></li><li><p>The organization&#8217;s quality bar</p></li><li><p>The future maintainer</p></li></ol><p>When the loop works, it can feel magical. A feature appears. A test is generated. A migration is drafted. A bug is explored. A refactor is attempted. A library is explained. A tedious transformation is done in minutes.</p><p>But &#8220;it feels magical&#8221; is not an abstraction criterion.</p><p>A slot machine also hides complexity. That does not make it a financial planning abstraction.</p><h2>The claim I am rejecting</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LD0s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F674d117b-f4ed-45cf-935d-1da858e80c66_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LD0s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F674d117b-f4ed-45cf-935d-1da858e80c66_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!LD0s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F674d117b-f4ed-45cf-935d-1da858e80c66_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!LD0s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F674d117b-f4ed-45cf-935d-1da858e80c66_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!LD0s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F674d117b-f4ed-45cf-935d-1da858e80c66_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LD0s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F674d117b-f4ed-45cf-935d-1da858e80c66_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/674d117b-f4ed-45cf-935d-1da858e80c66_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:618079,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F674d117b-f4ed-45cf-935d-1da858e80c66_1024x559.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_!LD0s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F674d117b-f4ed-45cf-935d-1da858e80c66_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!LD0s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F674d117b-f4ed-45cf-935d-1da858e80c66_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!LD0s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F674d117b-f4ed-45cf-935d-1da858e80c66_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!LD0s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F674d117b-f4ed-45cf-935d-1da858e80c66_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I am not rejecting the use of LLM agents.</p><p>I am rejecting this claim:</p><p>&#8220;LLM agentic development is just another abstraction layer over software development.&#8221;</p><p>That claim is too vague to be useful and too comforting to be harmless.</p><p>It suggests that the main historical pattern is &#8220;we stop caring about lower-level details.&#8221; That is false. The real pattern is more demanding: we build a model that lets us ignore some details because other details have been stabilized, specified, automated, tested, and bounded.</p><p>The abstraction claim also smuggles in a managerial fantasy. If agents are an abstraction layer over development, then maybe teams can buy the layer and reduce the need for deep engineering knowledge. Maybe the organization can route around slow humans. Maybe review becomes optional. Maybe design becomes prompting. Maybe tests are generated after the fact. Maybe senior engineers become supervisors of synthetic output rather than stewards of system integrity.</p><p>That fantasy is not new.</p><p>It is the old dream of automatic programming with a fresh coat of venture capital paint.</p><p>Frederick Brooks warned that there is no single technology or management technique that removes the essential difficulties of software. His point is still brutal: much of software&#8217;s difficulty is not typing code. It is understanding the problem, preserving conceptual integrity, managing change, conforming to other systems, and reasoning about a structure with too many interacting states.</p><p>LLM agents may reduce some accidental difficulty. They may help with ceremony, scaffolding, translation, boilerplate, exploration, and local edits.</p><p>But if they do not reduce the essential difficulty of understanding what should be built, why it should behave that way, how it interacts with the rest of the system, and how it will be safely changed later, they are not an abstraction over software development.</p><p>They are a new interface to a still-hard problem.</p><h2>The missing contract</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DUS_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1966345-f6f7-4b28-b789-5f4707b60654_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DUS_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1966345-f6f7-4b28-b789-5f4707b60654_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!DUS_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1966345-f6f7-4b28-b789-5f4707b60654_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!DUS_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1966345-f6f7-4b28-b789-5f4707b60654_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!DUS_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1966345-f6f7-4b28-b789-5f4707b60654_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DUS_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1966345-f6f7-4b28-b789-5f4707b60654_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c1966345-f6f7-4b28-b789-5f4707b60654_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:439048,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1966345-f6f7-4b28-b789-5f4707b60654_1024x559.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_!DUS_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1966345-f6f7-4b28-b789-5f4707b60654_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!DUS_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1966345-f6f7-4b28-b789-5f4707b60654_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!DUS_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1966345-f6f7-4b28-b789-5f4707b60654_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!DUS_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1966345-f6f7-4b28-b789-5f4707b60654_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A real abstraction answers the question:</p><p>What can I rely on?</p><p>With LLM agentic development, the answer is often uncomfortable.</p><p>Can I rely on the same prompt producing the same result tomorrow? Not fully.</p><p>Can I rely on the model understanding the domain rule the same way a team understands it after months of production incidents? No.</p><p>Can I rely on generated tests proving the generated code is correct? No.</p><p>Can I rely on a green test suite if the agent changed the tests, the implementation, and the assumptions together? No.</p><p>Can I rely on the agent preserving architectural boundaries unless they are explicitly encoded and checked? No.</p><p>Can I rely on the generated patch being maintainable by someone who did not watch the whole conversation? No.</p><p>Can I rely on the tool staying stable while the model, editor, plugin, context mechanism, pricing, rate limits, and agent framework evolve? No.</p><p>That does not mean the tool is useless. It means the abstraction claim is premature.</p><p>A useful abstraction reduces the number of details I must actively manage. A weak agentic setup often increases the number of details I must audit:</p><ol><li><p>Was the prompt precise enough?</p></li><li><p>Did the model use the right files?</p></li><li><p>Did retrieval miss a critical convention?</p></li><li><p>Did the agent change behavior outside the intended scope?</p></li><li><p>Did it introduce duplication?</p></li><li><p>Did it bypass an architectural boundary?</p></li><li><p>Did it generate tests that merely approve its own mistake?</p></li><li><p>Did it rely on a deprecated API?</p></li><li><p>Did it add a dependency we do not want?</p></li><li><p>Did it solve the visible symptom while damaging the domain model?</p></li><li><p>Did it produce code that compiles but cannot be owned?</p></li><li><p>Did it make the next change harder?<br>If I must ask all of that every time, I am not standing on a new abstraction layer.</p></li></ol><p>I am supervising a probabilistic contributor with access to my attention, my tools, and sometimes my codebase.</p><h2>Non-determinism is not a footnote</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HlPh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76b6845d-1523-45dd-91f6-c88e9561e62b_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HlPh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76b6845d-1523-45dd-91f6-c88e9561e62b_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!HlPh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76b6845d-1523-45dd-91f6-c88e9561e62b_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!HlPh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76b6845d-1523-45dd-91f6-c88e9561e62b_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!HlPh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76b6845d-1523-45dd-91f6-c88e9561e62b_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HlPh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76b6845d-1523-45dd-91f6-c88e9561e62b_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/76b6845d-1523-45dd-91f6-c88e9561e62b_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1323998,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76b6845d-1523-45dd-91f6-c88e9561e62b_1024x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HlPh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76b6845d-1523-45dd-91f6-c88e9561e62b_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!HlPh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76b6845d-1523-45dd-91f6-c88e9561e62b_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!HlPh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76b6845d-1523-45dd-91f6-c88e9561e62b_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!HlPh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76b6845d-1523-45dd-91f6-c88e9561e62b_1024x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Non-determinism is not a philosophical objection. It is an engineering property.</p><p>LLM APIs increasingly expose controls for reproducibility, such as seeds or system fingerprints, but those controls are not the same thing as a language specification, a type contract, or a deterministic compiler pipeline. They can improve repeatability in bounded settings. They do not turn an open-ended agent run into a stable abstraction over software development.</p><p>A compiler can have bugs. A database optimizer can choose a surprising plan. A distributed system can fail in strange ways. Mature abstractions are not perfect. But they are built around mechanisms that make behavior inspectable, reproducible, specified, constrained, or at least debuggable.</p><p>LLM agentic systems sit in a different place.</p><p>A prompt is not a specification in the same sense as a type contract, protocol, schema, or executable test. A model response is not a proof. A generated plan is not reliable execution. A successful run is not evidence that the next run will behave the same way.</p><p>That instability changes the economics of work.</p><p>When a tool is deterministic enough, we automate around it. When it is not, we supervise around it. Those are not the same motion.</p><p>Automation scales by moving human attention away from routine execution. Supervision consumes human attention to keep uncertain execution within acceptable bounds.</p><p>LLM agentic development often claims the first while quietly demanding the second.</p><p>That is the bill.</p><h2>Hallucination is not a leaky abstraction</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ENto!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6395015-ff71-4f5c-8178-dcc19b62907a_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ENto!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6395015-ff71-4f5c-8178-dcc19b62907a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ENto!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6395015-ff71-4f5c-8178-dcc19b62907a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ENto!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6395015-ff71-4f5c-8178-dcc19b62907a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ENto!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6395015-ff71-4f5c-8178-dcc19b62907a_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ENto!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6395015-ff71-4f5c-8178-dcc19b62907a_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f6395015-ff71-4f5c-8178-dcc19b62907a_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:439442,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6395015-ff71-4f5c-8178-dcc19b62907a_1024x559.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_!ENto!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6395015-ff71-4f5c-8178-dcc19b62907a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ENto!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6395015-ff71-4f5c-8178-dcc19b62907a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ENto!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6395015-ff71-4f5c-8178-dcc19b62907a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ENto!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6395015-ff71-4f5c-8178-dcc19b62907a_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>All non-trivial abstractions leak. That observation is useful, but it should not be abused.</p><p>When <em>Transmission Control Protocol</em> (TCP) abstracts over unreliable <em>Internet Protocol</em> (IP), the network can still leak through. You still need timeouts. You still need retries. You still need to understand latency, congestion, and partial failure when systems matter.</p><p>But TCP&#8217;s abstraction is not &#8220;sometimes I invent packets that sound right.&#8221;</p><p>It has a protocol.</p><p>LLM hallucination is different. A model can produce fluent, plausible, confident output that is factually unsupported, semantically wrong, or subtly inconsistent with the codebase. In software, that is not just a content problem. It is a trust-boundary problem.</p><p>Generated code can look right while violating a domain invariant. Generated tests can look professional while asserting the wrong behavior. Generated documentation can sound helpful while preserving a false mental model. Generated architecture advice can be stylistically convincing while ignoring constraints that exist only in production scars, team agreements, or old migration notes.</p><p>That is not an abstraction leak in the normal sense.</p><p>That is a system presenting unverified synthesis as if it were knowledge.</p><p>The danger is not that LLMs fail. All tools fail. The danger is that they fail in a way that is rhetorically smooth.</p><p>A compiler error is ugly, but honest.</p><p>A hallucinated explanation is beautiful, but dangerous.</p><h2>The infrastructure matters more than the slogan</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!w9zP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25111afc-f94c-4df6-9c12-9f2bd7704db9_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!w9zP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25111afc-f94c-4df6-9c12-9f2bd7704db9_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!w9zP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25111afc-f94c-4df6-9c12-9f2bd7704db9_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!w9zP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25111afc-f94c-4df6-9c12-9f2bd7704db9_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!w9zP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25111afc-f94c-4df6-9c12-9f2bd7704db9_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!w9zP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25111afc-f94c-4df6-9c12-9f2bd7704db9_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/25111afc-f94c-4df6-9c12-9f2bd7704db9_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:472751,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25111afc-f94c-4df6-9c12-9f2bd7704db9_1024x559.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_!w9zP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25111afc-f94c-4df6-9c12-9f2bd7704db9_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!w9zP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25111afc-f94c-4df6-9c12-9f2bd7704db9_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!w9zP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25111afc-f94c-4df6-9c12-9f2bd7704db9_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!w9zP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25111afc-f94c-4df6-9c12-9f2bd7704db9_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>People talk about agents as if the model were the product.</p><p>It is not.</p><p>An agentic development system is an infrastructure stack. It includes the model, the tool runner, the editor integration, the file system permissions, the sandbox, the memory system, the repository search, the dependency manager, the shell, the browser, the test runner, the policy layer, the audit trail, the logs, and the human operating model.</p><p>Change any of those and the system changes.</p><p>This is why the claim &#8220;the agent abstracts development&#8221; is so weak. The behavior depends heavily on the surrounding infrastructure.</p><p>A model with poor repository context is not the same as a model with precise retrieval. A model with unrestricted shell access is not the same as a model running in a sandbox. A model that can edit tests and code together is not the same as one that must satisfy externally owned tests. A model that can access production credentials is not the same as one that cannot.</p><p>The agent is not the abstraction.</p><p>At best, the abstraction is the controlled system around the agent: task boundaries, permissions, checks, traces, feedback, and ownership.</p><p>And most teams do not have that abstraction.</p><p>They have an editor plugin, a chat window, a half-written prompt, a few conventions, a fragile <em>Continuous Integration</em> (CI) pipeline, a senior engineer reviewing too much code, and a manager counting output.</p><p>That is not a new layer of software engineering.</p><p>That is a new shape of operational risk.</p><h2>The hidden detail is not the problem; hidden responsibility is</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AJF4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be99e04-6daa-4619-be41-f4f69023e912_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AJF4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be99e04-6daa-4619-be41-f4f69023e912_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!AJF4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be99e04-6daa-4619-be41-f4f69023e912_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!AJF4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be99e04-6daa-4619-be41-f4f69023e912_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!AJF4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be99e04-6daa-4619-be41-f4f69023e912_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AJF4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be99e04-6daa-4619-be41-f4f69023e912_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1be99e04-6daa-4619-be41-f4f69023e912_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:221292,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be99e04-6daa-4619-be41-f4f69023e912_1024x559.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_!AJF4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be99e04-6daa-4619-be41-f4f69023e912_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!AJF4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be99e04-6daa-4619-be41-f4f69023e912_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!AJF4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be99e04-6daa-4619-be41-f4f69023e912_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!AJF4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1be99e04-6daa-4619-be41-f4f69023e912_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The key mistake in the abstraction story is not that agents hide detail. All abstractions hide detail. The mistake is that agents often hide the work while leaving the responsibility untouched.</p><p>That is the difference.</p><p>A compiler hides register allocation, instruction selection, and many machine-level details. But it also gives the developer a language specification, error model, build process, and repeatable artifact.</p><p>A database hides storage layout, query execution, and indexing machinery. But it also gives transactions, isolation levels, constraints, query plans, and operational signals.</p><p>An agent hides context selection, inference, planning, tool choice, and intermediate reasoning. But in many setups, it does not give the team a stable semantic model, a bounded failure model, or an independent correctness contract.</p><p>The hidden detail is not what makes the agent risky.</p><p>The hidden responsibility is.</p><h2>We already learned this lesson with frameworks</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tsBw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9259d56-bb4a-41ed-956f-00c0d4cd91a1_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tsBw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9259d56-bb4a-41ed-956f-00c0d4cd91a1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!tsBw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9259d56-bb4a-41ed-956f-00c0d4cd91a1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!tsBw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9259d56-bb4a-41ed-956f-00c0d4cd91a1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!tsBw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9259d56-bb4a-41ed-956f-00c0d4cd91a1_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tsBw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9259d56-bb4a-41ed-956f-00c0d4cd91a1_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c9259d56-bb4a-41ed-956f-00c0d4cd91a1_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:889898,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9259d56-bb4a-41ed-956f-00c0d4cd91a1_1024x559.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_!tsBw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9259d56-bb4a-41ed-956f-00c0d4cd91a1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!tsBw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9259d56-bb4a-41ed-956f-00c0d4cd91a1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!tsBw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9259d56-bb4a-41ed-956f-00c0d4cd91a1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!tsBw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9259d56-bb4a-41ed-956f-00c0d4cd91a1_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This problem is not new.</p><p>Imagine building a business-critical system on top of a framework. The framework promises productivity. It gives structure, conventions, routing, persistence helpers, dependency injection, validation, security primitives, testing utilities, and a happy path.</p><p>That can be useful.</p><p>But now imagine that every few weeks the framework changes its public interfaces, shifts its conventions, deprecates common paths, changes configuration semantics, and forces your business code to move because the framework moved underneath it.</p><p>Would you call that a successful abstraction?</p><p>Probably not.</p><p>You would call it coupling.</p><p>You would ask why your business model is so exposed to framework churn. You would introduce boundaries. You would isolate framework-specific code. You would protect the domain from external volatility. You would stop pretending that convenience is the same as independence.</p><p>This is one of the reasons developers have spent years learning not to couple their core business logic directly to frameworks. We create ports. We isolate adapters. We distinguish domain decisions from infrastructure decisions. We accept that frameworks are useful, but we also know that framework convenience can become framework gravity.</p><p>Agentic development has the same problem.</p><p>If your development process depends on the quirks of a specific model, a specific agent runner, a specific prompt format, a specific tool-calling protocol, a specific editor integration, a specific context-selection strategy, and a specific vendor&#8217;s execution environment, then you have not escaped implementation detail.</p><p>You have acquired a new implementation detail.</p><p>Calling that an abstraction layer does not make the coupling disappear.</p><p>It only makes the coupling harder to see.</p><h2>The evidence is mixed, and that matters</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!64fQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F154aee2e-34d4-4f0b-9804-592064b702f3_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!64fQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F154aee2e-34d4-4f0b-9804-592064b702f3_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!64fQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F154aee2e-34d4-4f0b-9804-592064b702f3_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!64fQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F154aee2e-34d4-4f0b-9804-592064b702f3_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!64fQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F154aee2e-34d4-4f0b-9804-592064b702f3_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!64fQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F154aee2e-34d4-4f0b-9804-592064b702f3_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/154aee2e-34d4-4f0b-9804-592064b702f3_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:796540,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F154aee2e-34d4-4f0b-9804-592064b702f3_1024x559.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_!64fQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F154aee2e-34d4-4f0b-9804-592064b702f3_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!64fQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F154aee2e-34d4-4f0b-9804-592064b702f3_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!64fQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F154aee2e-34d4-4f0b-9804-592064b702f3_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!64fQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F154aee2e-34d4-4f0b-9804-592064b702f3_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The evidence around LLM coding tools is not a clean victory lap. It is context-dependent, and the context matters more than the demo.</p><p>There are controlled studies where coding assistants improve completion time on bounded programming tasks. One well-known GitHub Copilot study found that developers using Copilot completed a JavaScript <em>Hypertext Transfer Protocol</em> (HTTP) server task 55.8 percent faster than the control group.</p><p>That is real evidence. But it is evidence about a specific task shape, not proof that agentic development abstracts software engineering.</p><p>There are also studies that complicate the story.</p><p>METR studied 16 experienced open-source developers across 246 tasks in mature repositories they knew well. Participants expected AI to reduce completion time. After the study, they also believed it had helped. The measured result was different: the AI-assisted condition took 19 percent longer in that setting.</p><p>That does not prove AI coding tools are useless. It proves the serious point: perceived productivity, local output, and actual system-level throughput are not the same thing.</p><p>DORA and FAROS are looking through different windows.</p><p>DORA captures perception, capabilities, and organizational self-reporting. FAROS captures telemetry from engineering systems. One tells us how teams experience AI adoption and what capabilities they believe matter. The other looks at downstream signals such as review time, bugs, incidents, churn, and production strain.</p><p>The disagreement is useful because the article is precisely about that gap.</p><p>A team can feel faster while the system becomes harder to review. A developer can feel more productive while the codebase becomes harder to maintain. A dashboard can show more completed tasks while production absorbs more defects.</p><p>That gap is where the bill hides.</p><p>FAROS should not be treated as the final word. It is vendor telemetry from one platform, and observational telemetry cannot settle causality by itself. But it is still useful because it measures the part of the system that self-reported productivity often misses: review queues, churn, incidents, bugs, and production strain.</p><p>That makes it relevant to this article.</p><p>The question is not only whether AI helps developers produce more. It often does. The sharper question is whether the rest of the engineering system can absorb that output without turning acceleration into rework.</p><p>That is where the numbers become uncomfortable.</p><p>FAROS&#8217;s 2026 AI Engineering Report analyzes roughly two years of telemetry from 22,000 developers and 4,000 teams. The throughput numbers are real: task completion is up 34 percent, epics completed per developer are up 66 percent, and code-related tasks are up 210 percent. If all you count is output, the story looks great.</p><p>Then the bill arrives.</p><p>In the same report, bugs per developer are up 54 percent, the incidents-to-pull-request ratio is up 242.7 percent, median review time is up roughly five times, and pull requests merged without review are up 31.3 percent. Average pull request size is up 51.3 percent and average files edited per pull request are up 59.7 percent. For the subset of organizations instrumenting deployment frequency and lead time, deployment frequency is down and lead time is up sharply.</p><p>FAROS calls this &#8220;Acceleration Whiplash.&#8221; That phrase earns its place. It names the thing this article is arguing: the acceleration is real, but the system receiving the output was not designed to absorb it. More code is not the same as more trustworthy software. More completed tasks are not the same as less risk. More merged pull requests are not the same as delivery.</p><p>The important part is not that FAROS is the final word. The important part is that its failure pattern matches the mechanism: output rises locally, verification cost rises downstream, and the organization mistakes motion for progress until the review queue, incident queue, and maintenance queue start sending invoices.</p><p>Benchmarks such as SWE-bench Verified are also useful. SWE-bench Verified evaluates models and agents on real-world software issues from open-source repositories, and it gives the industry a better shared measurement surface than demos. But benchmark success is not the same as long-term product ownership.</p><p>Research on whether &#8220;solved&#8221; SWE-bench issues are really solved correctly adds another warning. A patch can satisfy the benchmark harness and still miss developer intent, introduce an overfitted fix, or rely on behavior that would be rejected by maintainers.</p><p>A generated patch can pass available tests and still fail to match developer intent. A solved issue can still create future maintenance cost. A benchmark can measure issue resolution, but it does not fully measure conceptual integrity, domain understanding, architectural fit, operability, support load, or the cost paid by the next developer.</p><p>This is exactly the kind of evidence we should expect if LLM tools reduce some local production cost while increasing verification and maintenance cost elsewhere.</p><p>The empirical picture does not support a simple slogan like &#8220;agents make developers obsolete&#8221; or &#8220;agents are useless.&#8221; Both are lazy.</p><p>The more accurate reading is harder:</p><p>LLM agents can be valuable in certain task classes, but the total outcome depends on task type, codebase familiarity, context quality, verification strength, review cost, security exposure, and long-term maintenance burden.</p><p>That is not marketing.</p><p>That is engineering.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;46558ba2-7ab1-4024-894c-870cf15d275c&quot;,&quot;caption&quot;:&quot;A restaurant buys a machine that can chop vegetables three times faster than any cook. At first, everyone celebrates. Prep is flying. Bowls fill up. Ingredients appear before anyone has time to ask for them. The manager walks past the counter and sees motion everywhere. The machine looks like productivity made visible.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Faster code, slower system&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-05-14T06:00:51.102Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!ENRk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/faster-code-slower-system&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:197182453,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>What to measure before believing the story</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8-vd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0980ac6-2194-4585-9742-66424062fd77_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8-vd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0980ac6-2194-4585-9742-66424062fd77_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!8-vd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0980ac6-2194-4585-9742-66424062fd77_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!8-vd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0980ac6-2194-4585-9742-66424062fd77_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!8-vd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0980ac6-2194-4585-9742-66424062fd77_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8-vd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0980ac6-2194-4585-9742-66424062fd77_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e0980ac6-2194-4585-9742-66424062fd77_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:579495,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0980ac6-2194-4585-9742-66424062fd77_1024x559.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_!8-vd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0980ac6-2194-4585-9742-66424062fd77_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!8-vd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0980ac6-2194-4585-9742-66424062fd77_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!8-vd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0980ac6-2194-4585-9742-66424062fd77_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!8-vd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0980ac6-2194-4585-9742-66424062fd77_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If an organization wants to claim that agents are raising the level of abstraction, it should be able to show where the cost went.</p><p>Not vibes. Not developer sentiment. Not a demo where a feature appears in three minutes. Not a dashboard that counts accepted suggestions and calls it productivity.</p><p>Real delivery evidence.</p><p>This is where DORA and <em>Accelerate</em> still matter. Deployment frequency, lead time for changes, change failure rate, and time to restore service are not perfect metrics, but they ask the right kind of question: did the system get better at delivering change safely? They do not confuse typing speed with delivery.</p><p>For agentic development, the measurement surface has to get wider:</p><ol><li><p>Pull request size by human-authored, AI-assisted, and agent-authored work</p></li><li><p>Review time by task class and risk level</p></li><li><p>Human review comments separated from agent review comments</p></li><li><p>Unreviewed merge rate</p></li><li><p>Reopened ticket rate</p></li><li><p>Bugs per pull request</p></li><li><p>Incidents per pull request</p></li><li><p>Lead time from commit to production</p></li><li><p>Work restarts after review or testing</p></li><li><p>Code churn by provenance, especially recently generated code deleted soon after merge</p></li><li><p>Test changes made together with implementation changes</p></li><li><p>Architectural boundary violations caught after the fact<br>These metrics are not a morality test. They are how a team finds the bill.</p></li></ol><p>If agent usage rises and pull request size rises, review time rises, incidents per pull request rise, and unreviewed merges rise, the organization did not buy an abstraction layer. It bought a faster way to manufacture review debt.</p><p>If agent usage rises while lead time improves, change failure rate stays controlled, review remains bounded, generated changes stay small, and human review comments feed back into better agent rules, then the organization may be building something real.</p><p>The difference is not enthusiasm.</p><p>It is whether the organization can see where the work moved.</p><h2>Where LLM agents are genuinely useful</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Lzqf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b93a4e9-fa52-4f88-a222-5b729210ba81_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Lzqf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b93a4e9-fa52-4f88-a222-5b729210ba81_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Lzqf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b93a4e9-fa52-4f88-a222-5b729210ba81_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Lzqf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b93a4e9-fa52-4f88-a222-5b729210ba81_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Lzqf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b93a4e9-fa52-4f88-a222-5b729210ba81_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Lzqf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b93a4e9-fa52-4f88-a222-5b729210ba81_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3b93a4e9-fa52-4f88-a222-5b729210ba81_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:428730,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b93a4e9-fa52-4f88-a222-5b729210ba81_1024x559.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_!Lzqf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b93a4e9-fa52-4f88-a222-5b729210ba81_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Lzqf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b93a4e9-fa52-4f88-a222-5b729210ba81_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Lzqf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b93a4e9-fa52-4f88-a222-5b729210ba81_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Lzqf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b93a4e9-fa52-4f88-a222-5b729210ba81_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A serious critique should not pretend there is no value.</p><p>There is value.</p><p>LLM agents can be useful when the cost of being slightly wrong is low, the feedback loop is fast, and the verification mechanism is independent of the generated output.</p><p>They are especially useful for exploration. An agent can summarize files, trace call paths, locate likely extension points, and generate hypotheses. The output should be treated as a guide, not as truth.</p><p>They are useful for first drafts. Boilerplate, adapters, migrations, test skeletons, documentation drafts, release-note drafts, and repetitive transformations can be accelerated when review is cheap and responsibility remains clear.</p><p>They are useful for mechanical change. Renames, API migrations, formatting changes, and broad but simple edits can fit the agentic loop when automated checks are strong and the scope is controlled.</p><p>They are useful for generating alternatives. Agents can propose multiple designs, compare trade-offs, and surface options a developer can evaluate.</p><p>They are useful for disposable prototypes. When learning is the goal and production quality is not yet required, agentic speed can be an advantage.</p><p>They are useful for test discovery. Agents can suggest missing cases, edge conditions, and characterization scenarios. They should not be trusted to define correctness alone.</p><p>They are useful for translation between representations. They can help move from notes to documentation, from examples to scaffolding, from API descriptions to client snippets, from logs to incident summaries, and from design conversations to decision records.</p><p>In these cases, the agent is not a mature abstraction over development.</p><p>It is a power tool. Power tools are useful. Power tools also remove fingers when treated as magic.</p><h2>Where the abstraction claim collapses</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!W7VL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2a61d51-2563-43d1-8e86-f09c0de4ff7d_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!W7VL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2a61d51-2563-43d1-8e86-f09c0de4ff7d_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!W7VL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2a61d51-2563-43d1-8e86-f09c0de4ff7d_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!W7VL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2a61d51-2563-43d1-8e86-f09c0de4ff7d_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!W7VL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2a61d51-2563-43d1-8e86-f09c0de4ff7d_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!W7VL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2a61d51-2563-43d1-8e86-f09c0de4ff7d_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d2a61d51-2563-43d1-8e86-f09c0de4ff7d_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:952711,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2a61d51-2563-43d1-8e86-f09c0de4ff7d_1024x559.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_!W7VL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2a61d51-2563-43d1-8e86-f09c0de4ff7d_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!W7VL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2a61d51-2563-43d1-8e86-f09c0de4ff7d_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!W7VL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2a61d51-2563-43d1-8e86-f09c0de4ff7d_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!W7VL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2a61d51-2563-43d1-8e86-f09c0de4ff7d_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The abstraction claim collapses when the work requires deep semantic ownership.</p><p>It collapses in domain-heavy systems where correctness lives in business rules that are not fully written down. It collapses in legacy codebases where the important knowledge is buried in production behavior, old incidents, implicit team conventions, and accidental dependencies.</p><p>It collapses in distributed systems where the difficulty is not the syntax of the change but the interaction between time, failure, retries, state, observability, and rollback.</p><p>It collapses in security-sensitive work where excessive agency, prompt injection, insecure output handling, plugin design, and overreliance are not academic risks. They are system design concerns.</p><p>It collapses in product development when the hard part is deciding what not to build. Agents can generate code faster than organizations can validate value. More code with more uncertainty is not progress. It is inventory.</p><p>It collapses in refactoring when the goal is not to move text around, but to preserve behavior while improving design. A refactoring is only a refactoring if behavior is preserved. A plausible diff is not enough.</p><p>It collapses in <em>Test-Driven Development</em> (TDD) when the agent writes the tests and the implementation from the same vague instruction, because the test may only encode the model&#8217;s misunderstanding. The discipline of TDD is not &#8220;tests exist.&#8221; It is using tests to create feedback, clarify design, and protect behavior through small steps.</p><p>It collapses in architecture when the generated solution optimizes local elegance while damaging global coherence. An agent can write a nice class that should not exist. It can introduce an abstraction that solves today&#8217;s prompt and becomes tomorrow&#8217;s tax.</p><p>The pattern is consistent.</p><p>Where independent verification is strong, agents can help.</p><p>Where verification depends on human judgment, domain knowledge, and architectural taste, the cost comes back.</p><h2>A test for calling something an abstraction layer</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0_jE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3261ecff-540e-43b7-9b5e-3d6a5934e52a_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0_jE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3261ecff-540e-43b7-9b5e-3d6a5934e52a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!0_jE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3261ecff-540e-43b7-9b5e-3d6a5934e52a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!0_jE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3261ecff-540e-43b7-9b5e-3d6a5934e52a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!0_jE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3261ecff-540e-43b7-9b5e-3d6a5934e52a_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0_jE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3261ecff-540e-43b7-9b5e-3d6a5934e52a_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3261ecff-540e-43b7-9b5e-3d6a5934e52a_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:876602,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3261ecff-540e-43b7-9b5e-3d6a5934e52a_1024x559.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_!0_jE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3261ecff-540e-43b7-9b5e-3d6a5934e52a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!0_jE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3261ecff-540e-43b7-9b5e-3d6a5934e52a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!0_jE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3261ecff-540e-43b7-9b5e-3d6a5934e52a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!0_jE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3261ecff-540e-43b7-9b5e-3d6a5934e52a_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A technology should not be called an abstraction layer just because it hides complexity.</p><p>It should pass a harder test.</p><ol><li><p>What can I safely stop thinking about?</p></li><li><p>Which details are hidden, and why are they safe to hide?</p></li><li><p>What semantics are preserved?</p></li><li><p>What contract does the layer provide?</p></li><li><p>What failure modes are explicit?</p></li><li><p>How do I observe and debug the boundary?</p></li><li><p>Can I replace the implementation without changing the caller&#8217;s mental model?</p></li><li><p>Does it reduce cognitive load, or does it move the burden to review?</p></li><li><p>Does it make errors easier to detect, or more fluent and harder to notice?</p></li><li><p>Who owns correctness when the layer fails?<br>Most current agentic development setups fail this test for software development as a whole.</p></li></ol><p>They may pass it for narrow, well-bounded workflows.</p><p>That distinction matters.</p><p>An agent that formats files under strict rules, inside a sandbox, with deterministic checks, and no authority to change behavior may be part of a useful automation layer.</p><p>An agent that generates a production change from a vague issue, edits code and tests together, invents missing context, and relies on a tired senior developer to catch the difference between plausible and correct is not an abstraction layer.</p><p>It is an attention loan.</p><p>And like all loans, someone pays interest.</p><h2>A better mental model</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!V992!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F140c1481-933d-4e5f-b399-4f0c0f1c7b57_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!V992!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F140c1481-933d-4e5f-b399-4f0c0f1c7b57_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!V992!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F140c1481-933d-4e5f-b399-4f0c0f1c7b57_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!V992!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F140c1481-933d-4e5f-b399-4f0c0f1c7b57_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!V992!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F140c1481-933d-4e5f-b399-4f0c0f1c7b57_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!V992!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F140c1481-933d-4e5f-b399-4f0c0f1c7b57_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/140c1481-933d-4e5f-b399-4f0c0f1c7b57_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:783601,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F140c1481-933d-4e5f-b399-4f0c0f1c7b57_1024x559.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_!V992!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F140c1481-933d-4e5f-b399-4f0c0f1c7b57_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!V992!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F140c1481-933d-4e5f-b399-4f0c0f1c7b57_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!V992!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F140c1481-933d-4e5f-b399-4f0c0f1c7b57_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!V992!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F140c1481-933d-4e5f-b399-4f0c0f1c7b57_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The better mental model is not &#8220;agent as abstraction layer.&#8221;</p><p>The better mental model is &#8220;agent as unreliable collaborator inside a controlled delivery system.&#8221;</p><p>That framing is less glamorous and more useful. It forces the right questions.</p><p>What tasks are safe to delegate? What checks are independent? What permissions are necessary? What should require human approval? What context must be supplied? What should be forbidden? What evidence tells us the tool improved total flow rather than local output? What happens when the model changes? What happens when a generated patch passes tests but damages maintainability? What happens when the agent is confidently wrong?</p><p>A controlled delivery system looks more like this:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BjyT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2922580-0afa-4928-a5dc-2ba043e04304_3653x388.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BjyT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2922580-0afa-4928-a5dc-2ba043e04304_3653x388.png 424w, https://substackcdn.com/image/fetch/$s_!BjyT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2922580-0afa-4928-a5dc-2ba043e04304_3653x388.png 848w, https://substackcdn.com/image/fetch/$s_!BjyT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2922580-0afa-4928-a5dc-2ba043e04304_3653x388.png 1272w, https://substackcdn.com/image/fetch/$s_!BjyT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2922580-0afa-4928-a5dc-2ba043e04304_3653x388.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BjyT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2922580-0afa-4928-a5dc-2ba043e04304_3653x388.png" width="1456" height="155" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a2922580-0afa-4928-a5dc-2ba043e04304_3653x388.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:155,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:69555,&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://emmanuelvalverderamos.substack.com/i/204099185?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2922580-0afa-4928-a5dc-2ba043e04304_3653x388.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_!BjyT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2922580-0afa-4928-a5dc-2ba043e04304_3653x388.png 424w, https://substackcdn.com/image/fetch/$s_!BjyT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2922580-0afa-4928-a5dc-2ba043e04304_3653x388.png 848w, https://substackcdn.com/image/fetch/$s_!BjyT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2922580-0afa-4928-a5dc-2ba043e04304_3653x388.png 1272w, https://substackcdn.com/image/fetch/$s_!BjyT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2922580-0afa-4928-a5dc-2ba043e04304_3653x388.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption"><a href="https://mermaid.live/edit#pako:eNplkjFvwjAQhf-KdTMMXTO0QjAUCaoKsbQOw8m-JAbHjmwHVAH_vfaltEOX07v3vosvTq6gvCaooLH-ojoMSWx2tRNij_EkSxHKYowHMZ8_i53JZinC0pnsoYDclvDdW6O-5IostZiMd2Jgh6kpZG7Rkktys9kKLIpj9jjdB1QkuQp0WmjTNIxMVkGWHalTlGunaaBc8qRii7kpndals6GLfB17dCJwM63MkpEVKRPzrteFUjSkl3vJH2Yhbh8Ub2LtErUBE8lfdfhHvvlbfvSRVJI7uvhwEj7kY4vxc2yR01vmi4UZtMFoqFIYaQY9hR5LC9cC15A66qmGKktNDY421VC7ex4b0H163z8mgx_bDqoGbczdOOi83cpg3vIPKfcUln50CaqnGZA2yYft9On5D7h_A9ILsmA">Open and edit this diagram in Mermaid Live</a></figcaption></figure></div><p>Notice where the abstraction might live.</p><p>It is not in the LLM response. It is in the policy, checks, boundaries, traceability, and ownership model around the agent.</p><p>This is where mature engineering can create real value. A team might define task classes where agentic execution is acceptable. It might require external tests before implementation. It might block changes outside allowed directories. It might run architectural fitness functions. It might record prompts, tool calls, diffs, and approvals. It might measure rework, review time, defect escape, cycle time, maintainability, and support burden.</p><p>Then, maybe, the organization has built something that behaves like an abstraction for a narrow class of work.</p><p>But that is not the same as saying &#8220;agents abstract development.&#8221;</p><p>It says:</p><p>&#8220;We built a constrained system where agents can perform specific work under explicit boundaries.&#8221;</p><p>That is a much smaller claim.</p><p>It is also a much more serious one.</p><h2>The sharp line</h2><p>There is a sharp line between these two statements:</p><ol><li><p>&#8220;LLM agents help me move faster in some tasks.&#8221;</p></li><li><p>&#8220;LLM agents are a new abstraction layer over software development.&#8221;<br>The first statement can be true.</p></li></ol><p>The second statement needs evidence and a contract.</p><p>The first can be local, personal, and contextual. The second is architectural. The first can be measured through task time, review time, defects, and subjective effort. The second must explain what complexity is hidden, what semantics are preserved, what failures are bounded, and what responsibilities have genuinely moved.</p><p>Most agentic development discourse jumps from the first to the second because the second sounds more historic. It gives the moment a destiny. It lets teams feel like they are climbing the same ladder that took us from assembly to high-level languages.</p><p>But history is not a ladder.</p><p>It is also a graveyard of tools that promised to remove thinking and instead created different thinking.</p><p>The mature move is not to reject the tool.</p><p>The mature move is to refuse the myth.</p><h2>Conclusion</h2><p>LLM agentic development is not worthless. It is not trivial. It is not a toy. It can be genuinely useful, and in some contexts it can change the economics of software work.</p><p>But useful does not mean abstract.</p><p>A real abstraction gives us a model strong enough to think with. It reduces relevant cognitive load. It preserves semantics. It defines boundaries. It exposes failure in ways we can manage. It lets us ignore selected details because the system has earned that trust.</p><p>Current LLM agents have not earned that status for software development as a whole. Today, they are better understood as probabilistic execution loops that can generate, transform, and explore under supervision. Their value depends on task selection, context quality, independent verification, security boundaries, and the cost of review and maintenance.</p><p>The bullshit is not using LLM agents.</p><p>The bullshit is pretending that the unpaid verification work disappeared because we moved it behind a prompt.</p><p>If a team wants to use agents well, it should stop asking whether agents are the future and start asking harder questions.</p><p>What work are we delegating? What risk are we accepting? What checks are independent? What knowledge are we losing? What rework are we creating? What would prove that this improved the system rather than just increasing output?</p><p>That is the difference between engineering and self-deception.</p><p>Agents may help us move faster. They may help us explore, draft, transform, and automate. But speed is not abstraction. Fluency is not correctness. Delegation is not a contract.</p><p>A real abstraction lets us reason safely at another level.</p><p>Current agentic development does not do that for software development as a whole. It can be part of a controlled engineering system, but it is not itself the layer that makes the hard parts disappear.</p><p>The unpaid work did not vanish.</p><p>It moved into review, verification, security, governance, and maintenance.</p><p>Calling that an abstraction layer is not technical clarity.</p><p>It is self-deception with better branding.</p><p>An abstraction layer is not a curtain.</p><p>It is a contract.</p><h2>References</h2><ul><li><p>Abelson, Harold; Sussman, Gerald Jay. <em>Structure and Interpretation of Computer Programs</em>. <a href="https://web.mit.edu/6.001/6.037/sicp.pdf">https://web.mit.edu/6.001/6.037/sicp.pdf</a></p></li><li><p>Anthropic. <em>Building effective agents</em>. <a href="https://www.anthropic.com/engineering/building-effective-agents">https://www.anthropic.com/engineering/building-effective-agents</a></p></li><li><p>Anthropic. <em>Building effective AI agents</em>. <a href="https://www.anthropic.com/research/building-effective-agents">https://www.anthropic.com/research/building-effective-agents</a></p></li><li><p>Autio, Chloe; Schwartz, Reva; Dunietz, Jesse; Jain, Shomik; Stanley, Martin; Tabassi, Elham; Hall, Patrick; Roberts, Kamie. <em>Artificial Intelligence Risk Management Framework: Generative Artificial Intelligence Profile</em>. <a href="https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence">https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence</a></p></li><li><p>Becker, Joel; Rush, Nate; Barnes, Beth; Rein, David. <em>Measuring the impact of early-2025 AI on experienced open-source developer productivity</em>. <a href="https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/">https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/</a></p></li><li><p>Becker, Joel; Rush, Nate; Barnes, Elizabeth; Rein, David. <em>Measuring the impact of early-2025 AI on experienced open-source developer productivity</em>. <a href="https://arxiv.org/abs/2507.09089">https://arxiv.org/abs/2507.09089</a></p></li><li><p>Bergmann, Sebastian. <em>Beyond Best Practices</em>. <a href="https://phpunit.expert/articles/beyond-best-practices.html">https://phpunit.expert/articles/beyond-best-practices.html</a></p></li><li><p>Bender, Emily M.; Gebru, Timnit; McMillan-Major, Angelina; Shmitchell, Shmargaret. <em>On the dangers of stochastic parrots: Can language models be too big</em>. <a href="https://dl.acm.org/doi/10.1145/3442188.3445922">https://dl.acm.org/doi/10.1145/3442188.3445922</a></p></li><li><p>Bommasani, Rishi; Hudson, Drew A.; Adeli, Ehsan; Altman, Russ; Arora, Simran; von Arx, Sydney; Bernstein, Michael S.; Bohg, Jeannette; Bosselut, Antoine; Brunskill, Emma; et al. <em>On the opportunities and risks of foundation models</em>. <a href="https://arxiv.org/abs/2108.07258">https://arxiv.org/abs/2108.07258</a></p></li><li><p>Brooks, Frederick P. <em>No Silver Bullet: Essence and accidents of software engineering</em>. <a href="https://www.cs.unc.edu/techreports/86-020.pdf">https://www.cs.unc.edu/techreports/86-020.pdf</a></p></li><li><p>Dijkstra, Edsger W. <em>The humble programmer</em>. <a href="https://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340.html">https://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340.html</a></p></li><li><p>DORA. <em>DORA metrics: The four keys</em>. <a href="https://dora.dev/guides/dora-metrics-four-keys/">https://dora.dev/guides/dora-metrics-four-keys/</a></p></li><li><p>DORA. <em>State of AI-assisted software development 2025</em>. <a href="https://dora.dev/dora-report-2025/">https://dora.dev/dora-report-2025/</a></p></li><li><p>Faros AI. <em>AI Engineering Report 2026: The Acceleration Whiplash</em>. <a href="https://pages.faros.ai/hubfs/AI_Engineering_Report_2026_The_Acceleration_Whiplash_Faros.pdf">https://pages.faros.ai/hubfs/AI_Engineering_Report_2026_The_Acceleration_Whiplash_Faros.pdf</a></p></li><li><p>Forsgren, Nicole; Humble, Jez; Kim, Gene. <em>Accelerate: The science of lean software and DevOps</em>. <a href="https://itrevolution.com/product/accelerate/">https://itrevolution.com/product/accelerate/</a></p></li><li><p>Huang, Lei; Yu, Weijiang; Ma, Weitao; Zhong, Weihong; Feng, Zhangyin; Wang, Haotian; Chen, Qianglong; Peng, Weihua; Feng, Xiaocheng; Qin, Bing; Liu, Ting. <em>A survey on hallucination in large language models: Principles, taxonomy, challenges, and open questions</em>. <a href="https://arxiv.org/abs/2311.05232">https://arxiv.org/abs/2311.05232</a></p></li><li><p>Khononov, Vlad. <em>Balancing coupling in software design</em>. <a href="https://www.oreilly.com/library/view/balancing-coupling-in/9780137353514/">https://www.oreilly.com/library/view/balancing-coupling-in/9780137353514/</a></p></li><li><p>Liskov, Barbara. <em>Data abstraction and hierarchy</em>. <a href="https://www.cs.tufts.edu/~nr/cs257/archive/barbara-liskov/data-abstraction-and-hierarchy.pdf">https://www.cs.tufts.edu/~nr/cs257/archive/barbara-liskov/data-abstraction-and-hierarchy.pdf</a></p></li><li><p>Liu, Jiawei; et al. <em>Large Language Model-based agents for software engineering: A survey</em>. <a href="https://arxiv.org/abs/2409.02977">https://arxiv.org/abs/2409.02977</a></p></li><li><p>Microsoft. <em>Reproducible output with Azure OpenAI Service</em>. <a href="https://learn.microsoft.com/en-us/azure/foundry-classic/openai/how-to/reproducible-output">https://learn.microsoft.com/en-us/azure/foundry-classic/openai/how-to/reproducible-output</a></p></li><li><p>OpenAI. <em>Advanced usage: Reproducible outputs</em>. <a href="https://developers.openai.com/api/docs/guides/advanced-usage">https://developers.openai.com/api/docs/guides/advanced-usage</a></p></li><li><p>OpenAI. <em>Agents SDK</em>. <a href="https://platform.openai.com/docs/guides/agents-sdk">https://platform.openai.com/docs/guides/agents-sdk</a></p></li><li><p>OpenAI. <em>Responses API tools</em>. <a href="https://platform.openai.com/docs/guides/tools">https://platform.openai.com/docs/guides/tools</a></p></li><li><p>OWASP Foundation. <em>OWASP Top 10 for LLM applications</em>. <a href="https://owasp.org/www-project-top-10-for-large-language-model-applications/">https://owasp.org/www-project-top-10-for-large-language-model-applications/</a></p></li><li><p>Parnas, David L. <em>On the criteria to be used in decomposing systems into modules</em>. <a href="https://wstomv.win.tue.nl/edu/2ip30/references/criteria_for_modularization.pdf">https://wstomv.win.tue.nl/edu/2ip30/references/criteria_for_modularization.pdf</a></p></li><li><p>Peng, Sida; Kalliamvakou, Eirini; Cihon, Peter; Demirer, Mert. <em>The impact of AI on developer productivity: Evidence from GitHub Copilot</em>. <a href="https://arxiv.org/abs/2302.06590">https://arxiv.org/abs/2302.06590</a></p></li><li><p>Schick, Timo; Dwivedi-Yu, Jane; Dessi, Roberto; Raileanu, Roberta; Lomeli, Maria; Zettlemoyer, Luke; Cancedda, Nicola; Scialom, Thomas. <em>Toolformer: Language models can teach themselves to use tools</em>. <a href="https://arxiv.org/abs/2302.04761">https://arxiv.org/abs/2302.04761</a></p></li><li><p>Spolsky, Joel. <em>The law of leaky abstractions</em>. <a href="https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/">https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/</a></p></li><li><p>SWE-bench. <em>SWE-bench Verified</em>. <a href="https://www.swebench.com/verified.html">https://www.swebench.com/verified.html</a></p></li><li><p>Wang, Lei; Ma, Chen; Feng, Xueyang; Zhang, Zeyu; Yang, Hao; Zhang, Jingsen; Chen, Zhiyuan; Tang, Jiakai; Chen, Xu; Lin, Yankai; Zhao, Wayne Xin; Wei, Zhewei; Wen, Ji-Rong. <em>A survey on Large Language Model based autonomous agents</em>. <a href="https://arxiv.org/abs/2308.11432">https://arxiv.org/abs/2308.11432</a></p></li><li><p>Wang, You; Pradel, Michael; Liu, Zhongxin. <em>Are &#8220;solved issues&#8221; in SWE-bench really solved correctly: An empirical study</em>. <a href="https://arxiv.org/abs/2503.15223">https://arxiv.org/abs/2503.15223</a></p></li><li><p>Wing, Jeannette M. <em>Computational thinking</em>. <a href="https://www.cs.cmu.edu/~15110-s13/Wing06-ct.pdf">https://www.cs.cmu.edu/~15110-s13/Wing06-ct.pdf</a></p></li><li><p>Wing, Jeannette M. <em>Computational thinking and thinking about computing</em>. <a href="https://people.cs.vt.edu/~kafura/CS6604/Papers/Wing-CT-Thinking-About-Computing.pdf">https://people.cs.vt.edu/~kafura/CS6604/Papers/Wing-CT-Thinking-About-Computing.pdf</a></p></li><li><p>Wright, Hyrum. <em>Hyrum&#8217;s Law</em>. https://www.hyrumslaw.com/</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Role tests are not about comparing implementations]]></title><description><![CDATA[How role tests turn interfaces from method shapes into executable behavioural contracts]]></description><link>https://emmanuelvalverderamos.substack.com/p/role-tests-are-not-about-comparing</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/role-tests-are-not-about-comparing</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 23 Jun 2026 05:00:35 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!JAKb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JAKb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JAKb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!JAKb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!JAKb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!JAKb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JAKb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:659017,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.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_!JAKb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!JAKb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!JAKb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!JAKb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d5c08d2-bec5-40ee-90e6-af7618c46e60_1024x559.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Interfaces often give us confidence earlier than they should.</p><p>At first, everything looks fine. The interface is clean, one implementation passes its tests, another implementation is easy to use in local development, and a third one might be useful in tests or in a different runtime context. The compiler is satisfied, the tests are green, and the boundary looks clean. The problem is that none of those things prove that every implementation means the same thing by <code>save</code> and <code>findById</code>.</p><p>That is the problem role tests help us examine.</p><p>An interface can tell us what can be called, but it cannot fully tell us what those calls are supposed to mean.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;edede6f0-2913-475d-8e04-b5d00e4adb8f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerRepository {
    fun findById(id: CustomerId): CustomerLookup
    fun save(customer: Customer)
}</code></pre></div><p>This interface tells us that a customer can be saved and later searched by id. Or, more carefully, it gives us a method named <code>save</code> and a method named <code>findById</code>. The rest of the meaning lives outside the type signature.</p><p>The interface does not answer all the behavioural questions clients eventually care about. What should happen when a customer does not exist? What does saving mean? Should two customers with different ids remain independent? And if several implementations exist, what makes them valid substitutes for the same clients?</p><p>A role test gives us a place to answer those questions.</p><p>The important nuance is that a role test is not mainly a way to compare two implementations with each other. It is a way to define the expected behaviour of a role, and then ask every implementation of that role to prove that it behaves correctly.</p><p>We are not asking whether implementation A behaves like implementation B. We are asking whether implementation A satisfies the role, and whether implementation B satisfies the same role.</p><p>When multiple implementations pass the same reusable behavioural tests, we gain confidence that they honour the same contract. Not only the same method names, but the same meaning.</p><h2>TL;DR</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!facS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35fb2774-a9da-40fc-a478-e6a581ffb4e4_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!facS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35fb2774-a9da-40fc-a478-e6a581ffb4e4_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!facS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35fb2774-a9da-40fc-a478-e6a581ffb4e4_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!facS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35fb2774-a9da-40fc-a478-e6a581ffb4e4_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!facS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35fb2774-a9da-40fc-a478-e6a581ffb4e4_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!facS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35fb2774-a9da-40fc-a478-e6a581ffb4e4_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/35fb2774-a9da-40fc-a478-e6a581ffb4e4_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1753758,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35fb2774-a9da-40fc-a478-e6a581ffb4e4_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!facS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35fb2774-a9da-40fc-a478-e6a581ffb4e4_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!facS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35fb2774-a9da-40fc-a478-e6a581ffb4e4_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!facS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35fb2774-a9da-40fc-a478-e6a581ffb4e4_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!facS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35fb2774-a9da-40fc-a478-e6a581ffb4e4_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p><strong>An interface defines shape, but a role test defines expected behaviour.</strong><br>A method signature can say <code>save(customer)</code>, but it cannot fully say what must be true after saving. Role tests make that meaning executable.</p></li><li><p><strong>Role tests do not primarily compare implementations against each other.</strong><br>They compare each implementation against the same behavioural contract. If several implementations pass the same role test, they are compatible with the same role.</p></li><li><p><strong>Roles are often discovered from the client side.</strong><br>A client object needs help, sends messages to a collaborator, and those messages start revealing a role. The interface may be written later, but the role begins to appear from use.</p></li><li><p><strong>A role test can be implemented in different ways.</strong><br>In this article, I use a Kotlin interface with default test methods. That is only one mechanism. The important idea is the reusable behavioural suite, not the specific reuse technique.</p></li><li><p><strong>They are useful for any interface with multiple meaningful implementations.</strong><br>A fake and a production adapter are a common example, but not the only one. Role tests apply whenever several implementations claim to satisfy the same behavioural contract.</p></li><li><p><strong>A role test should describe externally visible behaviour, not implementation details.</strong><br>The test should not care whether the repository uses a map, a database, a file, or an HTTP service. It should care about what the role promises to its clients.</p></li><li><p><strong>The main design benefit is semantic pressure.</strong><br>Role tests force us to say what an abstraction means. That makes weak interfaces, vague responsibilities, and misleading implementations easier to detect.</p></li></ul><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Index</h2><ol><li><p>The problem with interfaces</p></li><li><p>What a role test is</p></li><li><p>What a role test is not</p></li><li><p>Roles are discovered from the client side</p></li><li><p>A small example in Kotlin</p></li><li><p>Testing two implementations with the same role test</p></li><li><p>Why this matters for every implementation of an interface</p></li><li><p>What should go into a role test?</p></li><li><p>What should not go into a role test?</p></li><li><p>Role tests and object-oriented design</p></li><li><p>Common mistakes</p></li><li><p>Conclusion</p></li><li><p>References</p></li></ol><h2>1. The problem with interfaces</h2><p>Interfaces are useful because they let us name a role without committing every client to one concrete implementation. That is one of the reasons they are so common around persistence, clocks, payment gateways, event publishers, file systems, external APIs, and other boundaries where substitution matters.</p><p>But an interface is also incomplete.</p><p>It defines what can be called, not everything those calls are expected to mean.</p><p>Take this example again:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;0dfd4602-4e1c-4407-b94a-0e8e319ad90a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerRepository {
    fun findById(id: CustomerId): CustomerLookup
    fun save(customer: Customer)
}</code></pre></div><p>The shape is clear, but the behaviour is still partly implicit. We can infer that <code>save</code> stores a customer and that <code>findById</code> retrieves one, but the interface does not explicitly define all the behavioural rules around those operations.</p><p>What should happen after saving a customer? What happens if two customers are saved? What happens if the same customer is saved twice? Which parts of the customer must be preserved? Are failures represented as exceptions, sealed results, nullable results, or something else?</p><p>Some of those answers may feel obvious because we understand the word &#8220;repository&#8221;. But that is exactly the problem: a lot of design knowledge hides inside naming conventions, team habits, framework expectations, and assumptions that are never made executable.</p><p>The compiler can check that an implementation has the right methods. It cannot check that those methods mean what the clients think they mean.</p><p>A role test can help.</p><p>The interface gives the role a shape. The role test gives the role a behavioural meaning.</p><p>The first shift is this:</p><blockquote><p>An interface is not the whole contract. It is only part of the contract.</p></blockquote><p>The real contract also includes behaviour.</p><h2>2. What a role test is</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1_aM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9d0f54-1020-41b4-ac5b-6306ba0f394e_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1_aM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9d0f54-1020-41b4-ac5b-6306ba0f394e_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!1_aM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9d0f54-1020-41b4-ac5b-6306ba0f394e_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!1_aM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9d0f54-1020-41b4-ac5b-6306ba0f394e_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!1_aM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9d0f54-1020-41b4-ac5b-6306ba0f394e_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1_aM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9d0f54-1020-41b4-ac5b-6306ba0f394e_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc9d0f54-1020-41b4-ac5b-6306ba0f394e_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:630983,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9d0f54-1020-41b4-ac5b-6306ba0f394e_1024x559.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_!1_aM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9d0f54-1020-41b4-ac5b-6306ba0f394e_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!1_aM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9d0f54-1020-41b4-ac5b-6306ba0f394e_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!1_aM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9d0f54-1020-41b4-ac5b-6306ba0f394e_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!1_aM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9d0f54-1020-41b4-ac5b-6306ba0f394e_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A role test is a reusable test suite that defines the expected behaviour of a role. In many object-oriented designs, that role is represented by an interface.</p><p>More precisely:</p><blockquote><p>A role test is a reusable behavioural test suite that states what every valid implementation of an interface must make true for its clients.</p></blockquote><p>In responsibility-driven design terms, a role is not just a type name. A role is a set of related responsibilities within a collaboration. That is why role tests fit the concept so well: they do not test &#8220;a class&#8221;; they test whether an implementation fulfils the responsibilities expected from that role.</p><p>For example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;d0092350-87b7-4d35-817a-244fea35f10b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerRepository {
    fun findById(id: CustomerId): CustomerLookup
    fun save(customer: Customer)
}</code></pre></div><p>The role test says something stronger than the interface alone can say:</p><blockquote><p>Any valid <code>CustomerRepository</code> should behave like this.</p></blockquote><p>That is different from saying:</p><blockquote><p>This particular implementation should behave like this.</p></blockquote><p>The distinction matters because the same test suite can be applied to multiple implementations.</p><p>For example:</p><ul><li><p><code>InMemoryCustomerRepository</code></p></li><li><p><code>SqlCustomerRepository</code></p></li><li><p><code>FileCustomerRepository</code></p></li><li><p><code>HttpCustomerRepository</code></p></li></ul><p>All of them may implement <code>CustomerRepository</code>. If they do, and if they are meant to be valid substitutes for the same clients, they should pass the same role test.</p><p>That makes the role test a form of executable documentation. It tells future readers what the interface means in this codebase, not only what methods it exposes.</p><p>This is valuable because weak abstractions rarely announce themselves with invalid method signatures. Most of the time, they look fine at the type level. The weakness appears later, when different implementations interpret the same interface differently, when test doubles become too permissive, or when clients rely on behaviours that were never made explicit.</p><p>A role test gives those behaviours a home.</p><p>There is a related term here: contract test.</p><p>That term is useful, but it is also overloaded. In some contexts, contract testing refers to service contracts between distributed systems. In others, it refers to tests that verify an implementation against a shared abstraction. The phrase &#8220;role test&#8221; is useful because it keeps the focus on the object-oriented idea: a collaborator plays a role, and that role has behavioural expectations.</p><p>The role test is the executable specification of those expectations.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ieWQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2494f5-4488-4a72-a3d3-61cba2ff9c1b_902x1420.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ieWQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2494f5-4488-4a72-a3d3-61cba2ff9c1b_902x1420.png 424w, https://substackcdn.com/image/fetch/$s_!ieWQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2494f5-4488-4a72-a3d3-61cba2ff9c1b_902x1420.png 848w, https://substackcdn.com/image/fetch/$s_!ieWQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2494f5-4488-4a72-a3d3-61cba2ff9c1b_902x1420.png 1272w, https://substackcdn.com/image/fetch/$s_!ieWQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2494f5-4488-4a72-a3d3-61cba2ff9c1b_902x1420.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ieWQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2494f5-4488-4a72-a3d3-61cba2ff9c1b_902x1420.png" width="317" height="499.04656319290467" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a2494f5-4488-4a72-a3d3-61cba2ff9c1b_902x1420.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1420,&quot;width&quot;:902,&quot;resizeWidth&quot;:317,&quot;bytes&quot;:99479,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2494f5-4488-4a72-a3d3-61cba2ff9c1b_902x1420.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_!ieWQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2494f5-4488-4a72-a3d3-61cba2ff9c1b_902x1420.png 424w, https://substackcdn.com/image/fetch/$s_!ieWQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2494f5-4488-4a72-a3d3-61cba2ff9c1b_902x1420.png 848w, https://substackcdn.com/image/fetch/$s_!ieWQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2494f5-4488-4a72-a3d3-61cba2ff9c1b_902x1420.png 1272w, https://substackcdn.com/image/fetch/$s_!ieWQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2494f5-4488-4a72-a3d3-61cba2ff9c1b_902x1420.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>3. What a role test is not</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gYXE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0f7d141-b8e5-44cd-b88e-ef87e85e7228_2202x906.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gYXE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0f7d141-b8e5-44cd-b88e-ef87e85e7228_2202x906.png 424w, https://substackcdn.com/image/fetch/$s_!gYXE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0f7d141-b8e5-44cd-b88e-ef87e85e7228_2202x906.png 848w, https://substackcdn.com/image/fetch/$s_!gYXE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0f7d141-b8e5-44cd-b88e-ef87e85e7228_2202x906.png 1272w, https://substackcdn.com/image/fetch/$s_!gYXE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0f7d141-b8e5-44cd-b88e-ef87e85e7228_2202x906.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gYXE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0f7d141-b8e5-44cd-b88e-ef87e85e7228_2202x906.png" width="1456" height="599" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b0f7d141-b8e5-44cd-b88e-ef87e85e7228_2202x906.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:599,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:86175,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0f7d141-b8e5-44cd-b88e-ef87e85e7228_2202x906.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_!gYXE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0f7d141-b8e5-44cd-b88e-ef87e85e7228_2202x906.png 424w, https://substackcdn.com/image/fetch/$s_!gYXE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0f7d141-b8e5-44cd-b88e-ef87e85e7228_2202x906.png 848w, https://substackcdn.com/image/fetch/$s_!gYXE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0f7d141-b8e5-44cd-b88e-ef87e85e7228_2202x906.png 1272w, https://substackcdn.com/image/fetch/$s_!gYXE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0f7d141-b8e5-44cd-b88e-ef87e85e7228_2202x906.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A role test is not a direct implementation comparison.</p><p>It is tempting to describe it as &#8220;testing two implementations of the same interface against each other.&#8221; That description is close enough to be understandable, but it is not precise enough to be useful as a design idea.</p><p>If we frame role tests that way, one implementation can accidentally become the reference implementation, and the other one becomes a copy. Sometimes that is useful, but it is not the core of the idea.</p><p>A better framing is this:</p><blockquote><p>We define the expected behaviour of the role once, then run that same behaviour against every implementation.</p></blockquote><p>So instead of saying:</p><blockquote><p><code>InMemoryCustomerRepository</code> behaves like <code>SqlCustomerRepository</code>.</p></blockquote><p>We want to say:</p><blockquote><p><code>InMemoryCustomerRepository</code> satisfies <code>CustomerRepositoryRoleShould</code>.<br><code>SqlCustomerRepository</code> satisfies <code>CustomerRepositoryRoleShould</code>.</p></blockquote><p>The role is the reference. Not one implementation.</p><p>That changes the design conversation. If the SQL implementation and the in-memory implementation disagree, the answer is not automatically &#8220;make the in-memory implementation match SQL.&#8221; Maybe the in-memory version is wrong. Maybe the SQL implementation exposes accidental behaviour. Maybe the interface is hiding two different roles. Maybe the role test is missing an important rule.</p><p>The role test is not there to crown one implementation as truth. It is there to make the shared behavioural contract explicit.</p><p>It is also not mainly a trick to reduce duplicated tests.</p><p>Reducing duplication is a useful consequence. If two implementations need to satisfy the same behaviour, reusing the same tests is obviously better than copying test cases. But that is not the deepest reason role tests matter.</p><p>The deeper reason is that they force the team to state the behaviour of the role once, clearly, and then hold every implementation to that shared meaning.</p><h2>4. Roles are discovered from the client side</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nim0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d9ac66-52c7-4f95-b749-e47ed85b1820_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nim0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d9ac66-52c7-4f95-b749-e47ed85b1820_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!nim0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d9ac66-52c7-4f95-b749-e47ed85b1820_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!nim0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d9ac66-52c7-4f95-b749-e47ed85b1820_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!nim0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d9ac66-52c7-4f95-b749-e47ed85b1820_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nim0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d9ac66-52c7-4f95-b749-e47ed85b1820_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/65d9ac66-52c7-4f95-b749-e47ed85b1820_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:542243,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d9ac66-52c7-4f95-b749-e47ed85b1820_1024x559.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_!nim0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d9ac66-52c7-4f95-b749-e47ed85b1820_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!nim0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d9ac66-52c7-4f95-b749-e47ed85b1820_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!nim0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d9ac66-52c7-4f95-b749-e47ed85b1820_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!nim0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d9ac66-52c7-4f95-b749-e47ed85b1820_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>A role does not usually appear because we sat down and invented an interface in isolation.</p><p>Very often, it appears because a client object needs help.</p><p>A service needs somewhere to save a customer. A use case needs a way to publish an event. A policy needs a source of time. A workflow needs something that can charge a payment. In each case, the client is not asking for a concrete class first. It is asking for a collaborator that can play a useful role in a larger behaviour.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0Ypj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbfe92e-c360-49ba-9246-333f27e574b3_4122x236.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0Ypj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbfe92e-c360-49ba-9246-333f27e574b3_4122x236.png 424w, https://substackcdn.com/image/fetch/$s_!0Ypj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbfe92e-c360-49ba-9246-333f27e574b3_4122x236.png 848w, https://substackcdn.com/image/fetch/$s_!0Ypj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbfe92e-c360-49ba-9246-333f27e574b3_4122x236.png 1272w, https://substackcdn.com/image/fetch/$s_!0Ypj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbfe92e-c360-49ba-9246-333f27e574b3_4122x236.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0Ypj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbfe92e-c360-49ba-9246-333f27e574b3_4122x236.png" width="1456" height="83" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6bbfe92e-c360-49ba-9246-333f27e574b3_4122x236.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:83,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:68885,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbfe92e-c360-49ba-9246-333f27e574b3_4122x236.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_!0Ypj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbfe92e-c360-49ba-9246-333f27e574b3_4122x236.png 424w, https://substackcdn.com/image/fetch/$s_!0Ypj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbfe92e-c360-49ba-9246-333f27e574b3_4122x236.png 848w, https://substackcdn.com/image/fetch/$s_!0Ypj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbfe92e-c360-49ba-9246-333f27e574b3_4122x236.png 1272w, https://substackcdn.com/image/fetch/$s_!0Ypj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbfe92e-c360-49ba-9246-333f27e574b3_4122x236.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>That is why role tests fit naturally with test-driven design.</p><p>Imagine we are testing <code>RegisterCustomer</code>.</p><p>At first, the test does not need a database. It only needs to express that, once the customer is registered, something must remember that customer. That need may first appear as a test double. Later, it becomes a <code>CustomerRepository</code> interface. Later still, the role test captures what all repositories must honour.</p><p>At that moment, the test is not only describing a method call. It is starting to discover a role.</p><p>The interface may be written later, but the role starts appearing from use.</p><p>This is why the word &#8220;client&#8221; matters. A role is not defined only by the object that implements it. It is also shaped by the clients that depend on it. The contract exists between both sides: the client should only rely on the role&#8217;s advertised responsibilities, and the implementation should satisfy them.</p><p>This is a useful way to think about mock objects too. The important idea is not that we are mocking a class that already exists. The important idea is that the client&#8217;s interactions reveal what kind of collaborator it needs. The messages sent to a test double can help us discover a role, and a role test can later protect that role across implementations.</p><p>That changes how we think about interfaces.</p><p>A good interface is not just a list of operations an implementation happens to provide. It is a description of what clients need from a collaborator.</p><p>That is exactly the situation behind our small <code>CustomerRepository</code> example. We should not read the repository interface as something invented from the database outward. We should read it as something discovered from the needs of a client that wants to register, update, or retrieve customers.</p><p>This also connects role tests with substitutability. If several adapters claim to satisfy the same role, clients should not be affected by which adapter they receive. Each adapter may work differently inside, but all of them must honour the same behavioural contract.</p><p>That is the design pressure role tests create.</p><p>They are not only checking implementation compatibility. They are checking whether the role discovered from client needs remains stable across the objects that claim to fulfil it.</p><h2>5. A small example in Kotlin</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NaLI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c41c3c-637e-4edc-a5ef-f5a1d605962b_1731x1448.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NaLI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c41c3c-637e-4edc-a5ef-f5a1d605962b_1731x1448.png 424w, https://substackcdn.com/image/fetch/$s_!NaLI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c41c3c-637e-4edc-a5ef-f5a1d605962b_1731x1448.png 848w, https://substackcdn.com/image/fetch/$s_!NaLI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c41c3c-637e-4edc-a5ef-f5a1d605962b_1731x1448.png 1272w, https://substackcdn.com/image/fetch/$s_!NaLI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c41c3c-637e-4edc-a5ef-f5a1d605962b_1731x1448.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NaLI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c41c3c-637e-4edc-a5ef-f5a1d605962b_1731x1448.png" width="1456" height="1218" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/47c41c3c-637e-4edc-a5ef-f5a1d605962b_1731x1448.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1218,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:144600,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c41c3c-637e-4edc-a5ef-f5a1d605962b_1731x1448.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_!NaLI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c41c3c-637e-4edc-a5ef-f5a1d605962b_1731x1448.png 424w, https://substackcdn.com/image/fetch/$s_!NaLI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c41c3c-637e-4edc-a5ef-f5a1d605962b_1731x1448.png 848w, https://substackcdn.com/image/fetch/$s_!NaLI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c41c3c-637e-4edc-a5ef-f5a1d605962b_1731x1448.png 1272w, https://substackcdn.com/image/fetch/$s_!NaLI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47c41c3c-637e-4edc-a5ef-f5a1d605962b_1731x1448.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8NTQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b7f8f14-2964-4a63-9394-f1de6fe57548_1223x1232.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8NTQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b7f8f14-2964-4a63-9394-f1de6fe57548_1223x1232.png 424w, https://substackcdn.com/image/fetch/$s_!8NTQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b7f8f14-2964-4a63-9394-f1de6fe57548_1223x1232.png 848w, https://substackcdn.com/image/fetch/$s_!8NTQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b7f8f14-2964-4a63-9394-f1de6fe57548_1223x1232.png 1272w, https://substackcdn.com/image/fetch/$s_!8NTQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b7f8f14-2964-4a63-9394-f1de6fe57548_1223x1232.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8NTQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b7f8f14-2964-4a63-9394-f1de6fe57548_1223x1232.png" width="1223" height="1232" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8b7f8f14-2964-4a63-9394-f1de6fe57548_1223x1232.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1232,&quot;width&quot;:1223,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105207,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b7f8f14-2964-4a63-9394-f1de6fe57548_1223x1232.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_!8NTQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b7f8f14-2964-4a63-9394-f1de6fe57548_1223x1232.png 424w, https://substackcdn.com/image/fetch/$s_!8NTQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b7f8f14-2964-4a63-9394-f1de6fe57548_1223x1232.png 848w, https://substackcdn.com/image/fetch/$s_!8NTQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b7f8f14-2964-4a63-9394-f1de6fe57548_1223x1232.png 1272w, https://substackcdn.com/image/fetch/$s_!8NTQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b7f8f14-2964-4a63-9394-f1de6fe57548_1223x1232.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We will use a repository because the example is familiar, not because role tests are mainly about repositories.</p><p>The same idea applies to clocks, payment gateways, event publishers, file adapters, HTTP clients, caches, notification senders, search adapters, or any other interface with meaningful substitution pressure.</p><p>Let us use a small domain.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;31cdff8d-8cf4-43c8-b5a4-0fa7d0cbe05d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class CustomerId(
    val value: String
) {
    init {
        require(value.isNotBlank()) { "Customer id must not be blank." }
    }
}

data class Customer(
    val id: CustomerId,
    val name: String
)</code></pre></div><p>Now we need to decide how the role represents the result of a lookup.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uHOL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8273aca1-96c0-44f4-b083-784b99f44bd8_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uHOL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8273aca1-96c0-44f4-b083-784b99f44bd8_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uHOL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8273aca1-96c0-44f4-b083-784b99f44bd8_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uHOL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8273aca1-96c0-44f4-b083-784b99f44bd8_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uHOL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8273aca1-96c0-44f4-b083-784b99f44bd8_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uHOL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8273aca1-96c0-44f4-b083-784b99f44bd8_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8273aca1-96c0-44f4-b083-784b99f44bd8_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:664253,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8273aca1-96c0-44f4-b083-784b99f44bd8_1024x559.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_!uHOL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8273aca1-96c0-44f4-b083-784b99f44bd8_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uHOL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8273aca1-96c0-44f4-b083-784b99f44bd8_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uHOL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8273aca1-96c0-44f4-b083-784b99f44bd8_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uHOL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8273aca1-96c0-44f4-b083-784b99f44bd8_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In Kotlin, I prefer making absence explicit with a sealed result rather than returning <code>null</code> or introducing a <code>MissingCustomer</code> null object. A <code>null</code> return can be simple, but it hides meaning. A null object can be useful in some domains, but it can also create an object that looks like a customer while representing the absence of one. A sealed result makes the contract explicit: a lookup either found a customer or it did not.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;b889d5c9-b917-4c49-b298-0e12533ea6fe&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">sealed interface CustomerLookup {
    data class Found(val customer: Customer) : CustomerLookup
    data class Missing(val id: CustomerId) : CustomerLookup
}</code></pre></div><p>The repository interface becomes:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;ce33b691-ea01-4d85-929c-94473d3a71db&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerRepository {
    fun findById(id: CustomerId): CustomerLookup
    fun save(customer: Customer)
}</code></pre></div><p>Now the absence case is part of the role&#8217;s language. The role does not say &#8220;maybe there is a customer, maybe there is <code>null</code>.&#8221; It says &#8220;a lookup has two possible outcomes: found or missing.&#8221;</p><p>That is not a detail. It is part of the behavioural contract clients are allowed to rely on.</p><p>Now we write the role test. In this example, I will express it as a Kotlin interface with default test methods.</p><p>That is not a requirement of role testing. It is only a convenient way to write this kind of reusable behavioural suite in Kotlin with JUnit 5.</p><p>The important idea is not that the role test is an interface, an abstract class, or a specific framework feature. The important idea is that the same behavioural test suite can be executed against every implementation of the role.</p><p>Using an interface keeps the example aligned with the idea we are testing: <code>CustomerRepositoryRoleShould</code> describes the behaviour any <code>CustomerRepository</code> implementation must satisfy.</p><p>In the example, each test follows Arrange, Act, Assert. That is not a separate concept in role testing. It is simply the test structure I will use to keep each behavioural expectation readable. Role tests work best when they are boring and explicit: set up the implementation, exercise the role, and assert the externally visible behaviour.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;4c7226e4-dace-4afa-ba83-8acbf92ec991&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

interface CustomerRepositoryRoleShould {

    fun createRepository(): CustomerRepository

    @Test
    fun `find saved customer by id`() {
        // Arrange
        val repository = createRepository()
        val customer = Customer(
            id = CustomerId("customer-1"),
            name = "Alice"
        )

        // Act
        repository.save(customer)
        val found = repository.findById(customer.id)

        // Assert
        assertThat(found).isEqualTo(CustomerLookup.Found(customer))
    }

    @Test
    fun `return missing when customer does not exist`() {
        // Arrange
        val repository = createRepository()
        val unknownCustomerId = CustomerId("unknown")

        // Act
        val found = repository.findById(unknownCustomerId)

        // Assert
        assertThat(found).isEqualTo(CustomerLookup.Missing(unknownCustomerId))
    }

    @Test
    fun `keep saved customers separated by id`() {
        // Arrange
        val repository = createRepository()
        val alice = Customer(
            id = CustomerId("customer-1"),
            name = "Alice"
        )
        val bob = Customer(
            id = CustomerId("customer-2"),
            name = "Bob"
        )

        // Act
        repository.save(alice)
        repository.save(bob)

        val foundAlice = repository.findById(alice.id)
        val foundBob = repository.findById(bob.id)

        // Assert
        assertThat(foundAlice).isEqualTo(CustomerLookup.Found(alice))
        assertThat(foundBob).isEqualTo(CustomerLookup.Found(bob))
    }
}</code></pre></div><p>This test does not say anything about maps, SQL, transactions, files, HTTP calls, JSON, or connection pools. It only describes behaviour that should be true from the outside.</p><p>That is the point.</p><p>A role test protects behaviour, not implementation.</p><p>The naming convention is also intentional. The <code>Should</code> suffix belongs to the test interface, not to each individual test method.</p><p>That gives us a specification such as <code>CustomerRepositoryRoleShould</code>, where each method completes the behavioural idea:</p><blockquote><p><code>CustomerRepositoryRoleShould</code> find saved customer by id.<br><code>CustomerRepositoryRoleShould</code> return missing when customer does not exist.<br><code>CustomerRepositoryRoleShould</code> keep saved customers separated by id.</p></blockquote><p>This keeps the individual test names short while preserving the idea that the role itself is the subject of the specification.</p><h2>6. Testing two implementations with the same role test</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uzo_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb1f2c6d-446d-48e2-bfac-c8d0130eb068_2211x1096.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uzo_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb1f2c6d-446d-48e2-bfac-c8d0130eb068_2211x1096.png 424w, https://substackcdn.com/image/fetch/$s_!uzo_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb1f2c6d-446d-48e2-bfac-c8d0130eb068_2211x1096.png 848w, https://substackcdn.com/image/fetch/$s_!uzo_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb1f2c6d-446d-48e2-bfac-c8d0130eb068_2211x1096.png 1272w, https://substackcdn.com/image/fetch/$s_!uzo_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb1f2c6d-446d-48e2-bfac-c8d0130eb068_2211x1096.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uzo_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb1f2c6d-446d-48e2-bfac-c8d0130eb068_2211x1096.png" width="1456" height="722" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fb1f2c6d-446d-48e2-bfac-c8d0130eb068_2211x1096.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:722,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:121928,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb1f2c6d-446d-48e2-bfac-c8d0130eb068_2211x1096.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_!uzo_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb1f2c6d-446d-48e2-bfac-c8d0130eb068_2211x1096.png 424w, https://substackcdn.com/image/fetch/$s_!uzo_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb1f2c6d-446d-48e2-bfac-c8d0130eb068_2211x1096.png 848w, https://substackcdn.com/image/fetch/$s_!uzo_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb1f2c6d-446d-48e2-bfac-c8d0130eb068_2211x1096.png 1272w, https://substackcdn.com/image/fetch/$s_!uzo_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb1f2c6d-446d-48e2-bfac-c8d0130eb068_2211x1096.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Now we can create an in-memory implementation.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;a5908c5e-bc93-4668-8ff5-f4a149db23f4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class InMemoryCustomerRepository : CustomerRepository {

    private val customers = mutableMapOf&lt;CustomerId, Customer&gt;()

    override fun findById(id: CustomerId): CustomerLookup {
        val customer = customers[id]

        if (customer == null) {
            return CustomerLookup.Missing(id)
        }

        return CustomerLookup.Found(customer)
    }

    override fun save(customer: Customer) {
        customers[customer.id] = customer
    }
}</code></pre></div><p>Its test class is small because the behavioural tests already live in <code>CustomerRepositoryRoleShould</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;1b809d35-9a09-408e-9faa-a948fd97a38b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class InMemoryCustomerRepositoryShould : CustomerRepositoryRoleShould {

    override fun createRepository(): CustomerRepository {
        return InMemoryCustomerRepository()
    }
}</code></pre></div><p>Now imagine we also have a SQL implementation.</p><p>The SQL repository should not need to expose every detail of how a <code>ResultSet</code> becomes a domain result. Its responsibility is to execute the SQL query and use its internal machinery to translate the result into the repository&#8217;s domain language.</p><p>That mapping responsibility can be separated without making it part of the repository&#8217;s public boundary.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;99060884-6981-4924-96b0-c9e870163d64&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">import java.sql.ResultSet
import javax.sql.DataSource

class SqlCustomerRepository(
    private val dataSource: DataSource
) : CustomerRepository {

    override fun findById(id: CustomerId): CustomerLookup {
        dataSource.connection.use { connection -&gt;
            connection.prepareStatement(
                """
                SELECT id, name
                FROM customers
                WHERE id = ?
                """.trimIndent()
            ).use { statement -&gt;
                statement.setString(1, id.value)

                val resultSet = statement.executeQuery()

                return SqlCustomerLookupMapper.toCustomerLookup(
                    resultSet = resultSet,
                    requestedId = id
                )
            }
        }
    }

    override fun save(customer: Customer) {
        dataSource.connection.use { connection -&gt;
            connection.prepareStatement(
                """
                INSERT INTO customers (id, name)
                VALUES (?, ?)
                """.trimIndent()
            ).use { statement -&gt;
                statement.setString(1, customer.id.value)
                statement.setString(2, customer.name)
                statement.executeUpdate()
            }
        }
    }
}

private object SqlCustomerLookupMapper {

    fun toCustomerLookup(
        resultSet: ResultSet,
        requestedId: CustomerId
    ): CustomerLookup {
        if (!resultSet.next()) {
            return CustomerLookup.Missing(requestedId)
        }

        return CustomerLookup.Found(
            Customer(
                id = CustomerId(resultSet.getString("id")),
                name = resultSet.getString("name")
            )
        )
    }
}</code></pre></div><p>Notice that <code>SqlCustomerLookupMapper</code> is not injected into the repository. That is intentional.</p><p>It is an internal implementation detail of the SQL adapter, not a peer of the <code>CustomerRepository</code> role. Extracting a collaborator does not automatically mean promoting it to the constructor boundary.</p><p>This matters for the article because it shows the same design principle at a smaller level.</p><p><code>CustomerRepository</code> is the role shared by multiple implementations. <code>SqlCustomerLookupMapper</code> is not part of that role. As shown here, the mapper is private and exercised through the SQL repository behaviour. If it grows enough to deserve direct tests, that would be a signal to revisit its visibility and design.</p><p>That distinction is important.</p><p>The role test protects the shared behaviour of <code>CustomerRepository</code>.</p><p>The lookup mapper supports one concrete implementation.</p><p>The SQL test reuses the same role test.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;4d0d4118-cb4e-496d-827c-e620fa831045&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class SqlCustomerRepositoryShould : CustomerRepositoryRoleShould {

    override fun createRepository(): CustomerRepository {
        val dataSource = TestDatabase.createDataSource()

        TestDatabase.clean(dataSource)
        TestDatabase.migrate(dataSource)

        return SqlCustomerRepository(dataSource)
    }
}</code></pre></div><p>The concrete test class is also responsible for test isolation. The shared role test defines behaviour, but each implementation-specific test class must create a clean, known state for that implementation.</p><p>For an in-memory implementation, creating a new instance may be enough. For a database-backed implementation, the concrete test class must clean, migrate, recreate, or otherwise isolate the database state before each test. Without that isolation, one test can leak state into another, and the role test stops being a reliable specification.</p><p>Now both implementations must satisfy the same behavioural contract.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NXiA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdb6c2d8-55d1-4997-97e9-92119c319f42_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NXiA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdb6c2d8-55d1-4997-97e9-92119c319f42_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!NXiA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdb6c2d8-55d1-4997-97e9-92119c319f42_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!NXiA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdb6c2d8-55d1-4997-97e9-92119c319f42_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!NXiA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdb6c2d8-55d1-4997-97e9-92119c319f42_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NXiA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdb6c2d8-55d1-4997-97e9-92119c319f42_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fdb6c2d8-55d1-4997-97e9-92119c319f42_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:627233,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdb6c2d8-55d1-4997-97e9-92119c319f42_1024x559.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_!NXiA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdb6c2d8-55d1-4997-97e9-92119c319f42_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!NXiA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdb6c2d8-55d1-4997-97e9-92119c319f42_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!NXiA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdb6c2d8-55d1-4997-97e9-92119c319f42_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!NXiA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdb6c2d8-55d1-4997-97e9-92119c319f42_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The in-memory implementation can use a map. The SQL implementation can use a database and an internal lookup mapper. The role test does not care because those are implementation decisions, not role-level promises.</p><p>The same would be true if the second implementation were file-based, HTTP-based, cache-backed, or provided by another storage engine. The example uses an in-memory repository and a SQL repository because they are easy to understand, not because role tests are only about fakes or repositories.</p><p>This is where role tests become practical. They let us write the behavioural specification once, then reuse it against every implementation that claims to satisfy the same abstraction.</p><p>The important part is not that we used a Kotlin test interface. The same idea could be implemented with an abstract test class, parametrized tests, composition, or another mechanism supported by the testing framework.</p><p>The mechanism is secondary.</p><p>The reusable behavioural contract is the point.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Dv-I!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1215d269-7258-45d0-848d-9dc7ce2ec0df_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Dv-I!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1215d269-7258-45d0-848d-9dc7ce2ec0df_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Dv-I!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1215d269-7258-45d0-848d-9dc7ce2ec0df_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Dv-I!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1215d269-7258-45d0-848d-9dc7ce2ec0df_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Dv-I!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1215d269-7258-45d0-848d-9dc7ce2ec0df_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Dv-I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1215d269-7258-45d0-848d-9dc7ce2ec0df_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1215d269-7258-45d0-848d-9dc7ce2ec0df_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:590787,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1215d269-7258-45d0-848d-9dc7ce2ec0df_1024x559.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_!Dv-I!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1215d269-7258-45d0-848d-9dc7ce2ec0df_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Dv-I!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1215d269-7258-45d0-848d-9dc7ce2ec0df_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Dv-I!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1215d269-7258-45d0-848d-9dc7ce2ec0df_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Dv-I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1215d269-7258-45d0-848d-9dc7ce2ec0df_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>7. Why this matters for every implementation of an interface</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cvHp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbea108-22dd-4946-ad63-1e34c3350bfc_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cvHp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbea108-22dd-4946-ad63-1e34c3350bfc_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!cvHp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbea108-22dd-4946-ad63-1e34c3350bfc_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!cvHp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbea108-22dd-4946-ad63-1e34c3350bfc_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!cvHp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbea108-22dd-4946-ad63-1e34c3350bfc_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cvHp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbea108-22dd-4946-ad63-1e34c3350bfc_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2cbea108-22dd-4946-ad63-1e34c3350bfc_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:542243,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbea108-22dd-4946-ad63-1e34c3350bfc_1024x559.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_!cvHp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbea108-22dd-4946-ad63-1e34c3350bfc_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!cvHp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbea108-22dd-4946-ad63-1e34c3350bfc_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!cvHp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbea108-22dd-4946-ad63-1e34c3350bfc_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!cvHp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbea108-22dd-4946-ad63-1e34c3350bfc_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Fakes are a useful example, but they are not the whole story.</p><p>Role tests are useful whenever several implementations claim to satisfy the same interface.</p><p>For example, a <code>CustomerRepository</code> may have several implementations:</p><ul><li><p><code>InMemoryCustomerRepository</code></p></li><li><p><code>SqlCustomerRepository</code></p></li><li><p><code>FileCustomerRepository</code></p></li><li><p><code>HttpCustomerRepository</code></p></li></ul><p>Only one of those implementations may be a test double. Some may be production adapters. Some may exist because the system is migrating from one storage mechanism to another. Some may exist because different deployments use different infrastructure. Some may exist because the same product supports different integration modes.</p><p>The point is not whether one implementation is &#8220;real&#8221; and another is &#8220;fake&#8221;.</p><p>The point is that all of them claim to play the same role.</p><p>If they do, they should honour the same behavioural contract.</p><p>A fake repository that passes the same role test as the SQL repository is a common and useful example because it prevents the fake from becoming a fantasy object that only works in tests. But the same reasoning applies to any pair or group of implementations.</p><p>A filesystem-backed repository and a SQL-backed repository may also need to satisfy the same role. A sandbox payment gateway and a production payment gateway may also need to satisfy the same role. A system clock and a fixed clock may also need to satisfy the same role, at least for the subset of behaviour that belongs to the clock abstraction. A message publisher backed by Kafka and another backed by an in-memory bus may also need to satisfy the same role.</p><p>In all these cases, the role test is not about fakes. It is about substitutability.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bag2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0da44b22-1908-434d-88d9-f283570c2824_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bag2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0da44b22-1908-434d-88d9-f283570c2824_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!bag2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0da44b22-1908-434d-88d9-f283570c2824_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!bag2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0da44b22-1908-434d-88d9-f283570c2824_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!bag2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0da44b22-1908-434d-88d9-f283570c2824_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bag2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0da44b22-1908-434d-88d9-f283570c2824_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0da44b22-1908-434d-88d9-f283570c2824_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:714195,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0da44b22-1908-434d-88d9-f283570c2824_1024x559.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_!bag2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0da44b22-1908-434d-88d9-f283570c2824_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!bag2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0da44b22-1908-434d-88d9-f283570c2824_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!bag2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0da44b22-1908-434d-88d9-f283570c2824_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!bag2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0da44b22-1908-434d-88d9-f283570c2824_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If a client depends on <code>CustomerRepository</code>, the client should not need to know whether the implementation is backed by memory, SQL, a file, or an external service. Each implementation may work differently inside, but all valid implementations must preserve the behaviour promised by the role.</p><p>That is what the role test protects.</p><p>It does not remove the need for implementation-specific tests. A SQL repository still needs tests around migrations, mapping, transactions, constraints, or integration with the database. A filesystem repository may need tests around paths, permissions, and serialization. An HTTP adapter may need tests around status codes, retries, and network failures.</p><p>Those tests matter, but they are not role tests.</p><p>The role test protects the shared behaviour.</p><p>The implementation-specific tests protect the implementation-specific responsibilities.</p><p>This distinction is important because otherwise the idea can be read as if role testing were mainly a way to keep fakes honest. That is one valuable use case, but it is only an example of the larger idea.</p><p>Role testing applies whenever an interface represents a role and more than one implementation claims to fulfil it.</p><h2>8. What should go into a role test?</h2><p>A role test should include behaviours that all valid implementations must share.</p><p>A useful way to decide whether something belongs in a role test is to ask two questions:</p><ul><li><p>Under what conditions is this responsibility valid?</p></li><li><p>What should be true after the responsibility has been used?</p></li></ul><p>For <code>CustomerRepository</code>, &#8220;when a customer is saved&#8221; is the condition we set up, and &#8220;the same customer can later be found by id&#8221; is the aftereffect we expect. That is the kind of behaviour a role test should capture.</p><p>The role test does not discover the contract by itself. It makes explicit the part of the contract that the team has decided clients are allowed to rely on.</p><p>For a repository, that may include rules such as:</p><ul><li><p>A saved customer can be found by id.<br></p></li><li><p>An unknown customer produces a missing lookup result.</p></li><li><p>Two customers with different ids remain independent.</p></li><li><p>Saving a customer preserves the relevant customer data.</p></li><li><p>Saving the same id twice has a defined behaviour, if clients rely on that.</p></li><li><p>Invalid input is rejected consistently, if that belongs to the role.</p></li></ul><p>The most important phrase is &#8220;if that belongs to the role.&#8221;</p><p>Not every difference between implementations is a contract violation. Some differences are implementation details.</p><p>A SQL repository may use transactions while an in-memory repository does not. A database-backed adapter may care about migrations, indexes, isolation levels, or connection handling. Those details matter, but they do not automatically belong in the role test.</p><p>The example role test above does not define all possible repository behaviours. For instance, it does not define what happens when the same customer id is saved twice. That is deliberate. A role test should grow only when clients need that behaviour to be part of the contract.</p><p>If clients rely on saving the same id twice replacing the previous customer, that belongs in the role test. If clients rely on duplicate saves failing, that belongs in the role test. If clients do not rely on either behaviour, we should be careful not to smuggle in one implementation&#8217;s accidental behaviour as if it were part of the role.</p><p>The role test should stay at the level of meaning. It should answer one question:</p><blockquote><p>What must every valid implementation make true for its clients?</p></blockquote><p>Not this one:</p><blockquote><p>How does this implementation work internally?</p></blockquote><p>There is another important distinction here: the role test should not collect everything implementations happen to share. It should capture only the behaviours clients are allowed to rely on.</p><p>For a repository, role tests often focus on persistence semantics. For a clock, they may focus on time behaviour. For a payment gateway, they may focus on how payment requests are represented and how failures are reported. For a message publisher, they may focus on what is published and when.</p><p>The stronger the role, the easier it is to test. The weaker the role, the more the test exposes confusion.</p><p>That confusion can be useful. Sometimes the difficulty of writing a role test is feedback that the interface does not yet mean anything precise.</p><h2>9. What should not go into a role test?</h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!J05A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed8b228-85c3-4579-b1b2-8aed26bd57a5_4816x652.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!J05A!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed8b228-85c3-4579-b1b2-8aed26bd57a5_4816x652.png 424w, https://substackcdn.com/image/fetch/$s_!J05A!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed8b228-85c3-4579-b1b2-8aed26bd57a5_4816x652.png 848w, https://substackcdn.com/image/fetch/$s_!J05A!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed8b228-85c3-4579-b1b2-8aed26bd57a5_4816x652.png 1272w, https://substackcdn.com/image/fetch/$s_!J05A!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed8b228-85c3-4579-b1b2-8aed26bd57a5_4816x652.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!J05A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed8b228-85c3-4579-b1b2-8aed26bd57a5_4816x652.png" width="1456" height="197" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ed8b228-85c3-4579-b1b2-8aed26bd57a5_4816x652.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:197,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:160397,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed8b228-85c3-4579-b1b2-8aed26bd57a5_4816x652.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_!J05A!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed8b228-85c3-4579-b1b2-8aed26bd57a5_4816x652.png 424w, https://substackcdn.com/image/fetch/$s_!J05A!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed8b228-85c3-4579-b1b2-8aed26bd57a5_4816x652.png 848w, https://substackcdn.com/image/fetch/$s_!J05A!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed8b228-85c3-4579-b1b2-8aed26bd57a5_4816x652.png 1272w, https://substackcdn.com/image/fetch/$s_!J05A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed8b228-85c3-4579-b1b2-8aed26bd57a5_4816x652.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>A role test should not become a dump of every behaviour of every implementation.</p><p>That is a common mistake.</p><p>The role test is not the place for SQL-specific behaviour, cache-specific behaviour, file-system-specific behaviour, or performance assumptions unless those concerns are part of the role being promised.</p><p>For example, these do not belong in a generic <code>CustomerRepositoryRoleShould</code>:</p><ul><li><p>Creating the customers table</p></li><li><p>Using a specific SQL index</p></li><li><p>Retrying deadlocks</p></li><li><p>Serializing customer as JSON</p></li><li><p>Storing customer names in lowercase</p></li><li><p>Verifying that the SQL lookup mapper calls <code>resultSet.next()</code></p></li></ul><p>Some of those behaviours may need tests. But they are not role tests for <code>CustomerRepository</code>. They are implementation tests for a specific adapter or one of its internal collaborators.</p><p>This separation matters because a role test protects shared meaning, while an implementation test protects implementation-specific responsibilities.</p><p>Mixing them creates noise. It also makes the reusable test suite less reusable.</p><p>The role test should be as abstract as the role, and no more concrete than necessary.</p><p>That also means role tests are not always worth adding. If there is no meaningful substitution pressure, a role test may add more ceremony than value. A single implementation behind an interface that nobody substitutes, nobody discovers from client needs, and nobody expects to vary may not need a role test. In that case, the better question might be whether the interface itself is carrying enough design value.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MH4V!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c8a2ec-87c7-46ba-b962-42ddfd323497_1110x2300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MH4V!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c8a2ec-87c7-46ba-b962-42ddfd323497_1110x2300.png 424w, https://substackcdn.com/image/fetch/$s_!MH4V!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c8a2ec-87c7-46ba-b962-42ddfd323497_1110x2300.png 848w, https://substackcdn.com/image/fetch/$s_!MH4V!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c8a2ec-87c7-46ba-b962-42ddfd323497_1110x2300.png 1272w, https://substackcdn.com/image/fetch/$s_!MH4V!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c8a2ec-87c7-46ba-b962-42ddfd323497_1110x2300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MH4V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c8a2ec-87c7-46ba-b962-42ddfd323497_1110x2300.png" width="452" height="936.5765765765766" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/95c8a2ec-87c7-46ba-b962-42ddfd323497_1110x2300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2300,&quot;width&quot;:1110,&quot;resizeWidth&quot;:452,&quot;bytes&quot;:175154,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c8a2ec-87c7-46ba-b962-42ddfd323497_1110x2300.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_!MH4V!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c8a2ec-87c7-46ba-b962-42ddfd323497_1110x2300.png 424w, https://substackcdn.com/image/fetch/$s_!MH4V!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c8a2ec-87c7-46ba-b962-42ddfd323497_1110x2300.png 848w, https://substackcdn.com/image/fetch/$s_!MH4V!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c8a2ec-87c7-46ba-b962-42ddfd323497_1110x2300.png 1272w, https://substackcdn.com/image/fetch/$s_!MH4V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95c8a2ec-87c7-46ba-b962-42ddfd323497_1110x2300.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>10. Role tests and object-oriented design</h2><p>Role tests are not only a testing technique. They also push on design.</p><p>They force a question that object-oriented design often avoids:</p><blockquote><p>What role is this object playing?</p></blockquote><p>That question is stronger than:</p><blockquote><p>What class is this?</p></blockquote><p>A class is a piece of code. A role is a responsibility in a collaboration.</p><p>This is why role tests fit naturally with object-oriented design. Objects are not just containers of state. They participate in behaviour, collaborate with other objects, send messages, and make promises to their clients.</p><p>A role test captures one of those promises.</p><p>This is also why role tests can reveal weak abstractions. If an interface is hard to role-test, maybe the role is not clear. If every implementation needs exceptions to the shared tests, maybe the interface is hiding several different concepts. If the role test has to know too many implementation details, maybe the abstraction is leaking. If one implementation passes but another cannot, maybe one of them is not respecting the real constraints of the role, or maybe the role itself is poorly defined.</p><p>The <code>SqlCustomerLookupMapper</code> example also shows something useful about design boundaries. Extracting a collaborator does not automatically make it part of the shared role. A mapper may improve the SQL adapter&#8217;s internal design, but clients of <code>CustomerRepository</code> should not know it exists. The role test should therefore ignore it.</p><p>The point is not that role tests magically produce good design. They do not. The point is that they create useful pressure.</p><p>They make vague contracts harder to ignore.</p><p>They also remind us that object-oriented design is not mainly about drawing class boxes. It is about the roles objects play in collaboration. When we test a role, we are not only testing an API surface. We are testing a promise made to clients.</p><p>That promise is what allows substitution to remain safe.</p><h2>11. Common mistakes</h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LnUj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89724ef9-9d3c-46a9-9c21-2cfe4b631212_3652x748.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LnUj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89724ef9-9d3c-46a9-9c21-2cfe4b631212_3652x748.png 424w, https://substackcdn.com/image/fetch/$s_!LnUj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89724ef9-9d3c-46a9-9c21-2cfe4b631212_3652x748.png 848w, https://substackcdn.com/image/fetch/$s_!LnUj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89724ef9-9d3c-46a9-9c21-2cfe4b631212_3652x748.png 1272w, https://substackcdn.com/image/fetch/$s_!LnUj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89724ef9-9d3c-46a9-9c21-2cfe4b631212_3652x748.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LnUj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89724ef9-9d3c-46a9-9c21-2cfe4b631212_3652x748.png" width="1456" height="298" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/89724ef9-9d3c-46a9-9c21-2cfe4b631212_3652x748.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:298,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:196734,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89724ef9-9d3c-46a9-9c21-2cfe4b631212_3652x748.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_!LnUj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89724ef9-9d3c-46a9-9c21-2cfe4b631212_3652x748.png 424w, https://substackcdn.com/image/fetch/$s_!LnUj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89724ef9-9d3c-46a9-9c21-2cfe4b631212_3652x748.png 848w, https://substackcdn.com/image/fetch/$s_!LnUj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89724ef9-9d3c-46a9-9c21-2cfe4b631212_3652x748.png 1272w, https://substackcdn.com/image/fetch/$s_!LnUj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89724ef9-9d3c-46a9-9c21-2cfe4b631212_3652x748.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Mistake 1: Treating one implementation as the truth</h3><p>The SQL implementation is not automatically the truth. The in-memory implementation is not automatically the truth either. The same applies to any other implementation: file-based, HTTP-based, cache-backed, sandbox, production, or fake.</p><p>The role is the truth.</p><p>If two implementations disagree, the answer is not automatically &#8220;make one match the other.&#8221; Sometimes one implementation exposed accidental behaviour that should not belong to the role. Sometimes another implementation is too permissive. Sometimes the interface is wrong. Sometimes there are two roles hiding behind one interface.</p><p>The role test should help clarify the contract, not blindly copy one implementation.</p><h3>Mistake 2: Testing implementation details</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!O2KM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd2810b-ed64-4fa6-88d9-e43680c9d8f6_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!O2KM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd2810b-ed64-4fa6-88d9-e43680c9d8f6_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!O2KM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd2810b-ed64-4fa6-88d9-e43680c9d8f6_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!O2KM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd2810b-ed64-4fa6-88d9-e43680c9d8f6_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!O2KM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd2810b-ed64-4fa6-88d9-e43680c9d8f6_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!O2KM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd2810b-ed64-4fa6-88d9-e43680c9d8f6_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7cd2810b-ed64-4fa6-88d9-e43680c9d8f6_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:568539,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd2810b-ed64-4fa6-88d9-e43680c9d8f6_1024x559.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_!O2KM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd2810b-ed64-4fa6-88d9-e43680c9d8f6_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!O2KM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd2810b-ed64-4fa6-88d9-e43680c9d8f6_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!O2KM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd2810b-ed64-4fa6-88d9-e43680c9d8f6_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!O2KM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd2810b-ed64-4fa6-88d9-e43680c9d8f6_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This happens when the test starts asking how the implementation works instead of what the role promises.</p><p>For example, tests such as &#8220;store customer in internal map&#8221;, &#8220;call insert statement&#8221;, &#8220;open database connection&#8221;, or &#8220;use this lookup mapper&#8221; may be valid implementation tests, but they are not role tests.</p><p>A role test should stay outside the object and observe behaviour.</p><h3>Mistake 3: Making the role too large</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!m-YP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04aa59b0-65e8-4685-b3ee-b4c8f5d3b5e1_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!m-YP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04aa59b0-65e8-4685-b3ee-b4c8f5d3b5e1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!m-YP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04aa59b0-65e8-4685-b3ee-b4c8f5d3b5e1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!m-YP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04aa59b0-65e8-4685-b3ee-b4c8f5d3b5e1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!m-YP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04aa59b0-65e8-4685-b3ee-b4c8f5d3b5e1_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!m-YP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04aa59b0-65e8-4685-b3ee-b4c8f5d3b5e1_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/04aa59b0-65e8-4685-b3ee-b4c8f5d3b5e1_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:629827,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04aa59b0-65e8-4685-b3ee-b4c8f5d3b5e1_1024x559.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_!m-YP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04aa59b0-65e8-4685-b3ee-b4c8f5d3b5e1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!m-YP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04aa59b0-65e8-4685-b3ee-b4c8f5d3b5e1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!m-YP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04aa59b0-65e8-4685-b3ee-b4c8f5d3b5e1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!m-YP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04aa59b0-65e8-4685-b3ee-b4c8f5d3b5e1_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If the role test becomes huge, the role may be too broad.</p><p>For example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;0b723c90-0661-4d2f-aa2f-15dc8c5e2b6f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface CustomerRepository {
    fun findById(id: CustomerId): CustomerLookup
    fun save(customer: Customer)
    fun delete(id: CustomerId)
    fun search(criteria: CustomerSearchCriteria): List&lt;Customer&gt;
    fun exportCsv(): String
    fun sendCustomerReport()
}</code></pre></div><p>This is no longer just persistence. It mixes querying, exporting, and notification-like behaviour. A role test for this interface will probably feel awkward because the interface itself is awkward.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uto6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faaf20eac-4146-49f2-b243-32a5604a8251_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uto6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faaf20eac-4146-49f2-b243-32a5604a8251_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uto6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faaf20eac-4146-49f2-b243-32a5604a8251_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uto6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faaf20eac-4146-49f2-b243-32a5604a8251_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uto6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faaf20eac-4146-49f2-b243-32a5604a8251_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uto6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faaf20eac-4146-49f2-b243-32a5604a8251_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aaf20eac-4146-49f2-b243-32a5604a8251_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:590787,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faaf20eac-4146-49f2-b243-32a5604a8251_1024x559.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_!uto6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faaf20eac-4146-49f2-b243-32a5604a8251_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uto6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faaf20eac-4146-49f2-b243-32a5604a8251_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uto6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faaf20eac-4146-49f2-b243-32a5604a8251_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uto6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faaf20eac-4146-49f2-b243-32a5604a8251_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Test friction can be design feedback.</p><h3>Mistake 4: Using role tests to avoid integration tests</h3><p>Role tests do not replace integration tests.</p><p>A role test asks whether an implementation satisfies the behaviour expected from a role. An integration test asks whether real parts work together in a real or realistic environment.</p><p>A SQL repository can pass its role test and still fail in production because of configuration, migrations, permissions, connection pooling, infrastructure, or deployment mistakes. An HTTP adapter can pass the shared role test and still fail because of authentication, timeouts, status-code handling, or incompatible API changes.</p><p>Role tests are valuable, but they are not a complete testing strategy.</p><h3>Mistake 5: Creating interfaces only because role tests exist</h3><p>Role tests are useful when there is a meaningful role. They are not a reason to create interfaces everywhere.</p><p>If there is only one implementation and no meaningful substitution pressure, an interface may add ceremony without value.</p><p>The sequence should not be this:</p><blockquote><p>I want a role test, therefore I need an interface.</p></blockquote><p>It should be this:</p><blockquote><p>There is a role with multiple implementations or important substitution pressure, therefore a role test may help.</p></blockquote><p>Otherwise, role tests become another form of test architecture theatre.</p><h3>Mistake 6: Confusing the role test with the reuse mechanism</h3><p>Because many examples use abstract test classes, test interfaces, or parametrized tests, it is easy to confuse the idea with the mechanism.</p><p>That confusion leads to statements such as:</p><blockquote><p>A role test is an abstract test class.</p></blockquote><p>Or:</p><blockquote><p>A role test is a test interface.</p></blockquote><p>Neither statement is quite right.</p><p>A role test can be implemented with an abstract class, a test interface, parametrized tests, composition, or another mechanism. The role test is the reusable behavioural contract. The mechanism is only the way we share and execute that contract.</p><p>This distinction matters because different teams and frameworks may prefer different mechanisms. The mechanism should serve the idea, not define it.</p><h2>12. Conclusion</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VQaP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefb0dad-d48c-4402-9912-6f69c8350acd_4068x1276.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VQaP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefb0dad-d48c-4402-9912-6f69c8350acd_4068x1276.png 424w, https://substackcdn.com/image/fetch/$s_!VQaP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefb0dad-d48c-4402-9912-6f69c8350acd_4068x1276.png 848w, https://substackcdn.com/image/fetch/$s_!VQaP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefb0dad-d48c-4402-9912-6f69c8350acd_4068x1276.png 1272w, https://substackcdn.com/image/fetch/$s_!VQaP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefb0dad-d48c-4402-9912-6f69c8350acd_4068x1276.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VQaP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefb0dad-d48c-4402-9912-6f69c8350acd_4068x1276.png" width="1456" height="457" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cefb0dad-d48c-4402-9912-6f69c8350acd_4068x1276.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:457,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:227086,&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://emmanuelvalverderamos.substack.com/i/195733924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefb0dad-d48c-4402-9912-6f69c8350acd_4068x1276.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_!VQaP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefb0dad-d48c-4402-9912-6f69c8350acd_4068x1276.png 424w, https://substackcdn.com/image/fetch/$s_!VQaP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefb0dad-d48c-4402-9912-6f69c8350acd_4068x1276.png 848w, https://substackcdn.com/image/fetch/$s_!VQaP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefb0dad-d48c-4402-9912-6f69c8350acd_4068x1276.png 1272w, https://substackcdn.com/image/fetch/$s_!VQaP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefb0dad-d48c-4402-9912-6f69c8350acd_4068x1276.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A role test is a small idea with a large design consequence.</p><p>It says that an abstraction means something. Not only that it has methods, not only that the compiler accepts it, and not only that different implementations have the same shape.</p><p>An interface gives us shape. A role test gives us behaviour.</p><p>When multiple implementations pass the same role test, we gain confidence that they are not merely compatible with the compiler. They are compatible with the role they claim to play.</p><p>This matters whenever implementations can drift: a SQL repository and a filesystem repository, a production adapter and a fake, a real gateway and a sandbox gateway, a Kafka publisher and an in-memory publisher, a system clock and a fixed clock.</p><p>In all of those cases, the danger is not only that one implementation has a bug. The deeper danger is that the role itself becomes vague.</p><p>Role tests help prevent that vagueness from hiding behind clean method signatures. They make the contract executable. They give every implementation a standard to satisfy. They give adapters a shared behavioural baseline. And they give design conversations a sharper question:</p><blockquote><p>What does this role actually promise?</p></blockquote><p>That is the question worth keeping.</p><p>Because object-oriented design is not only about classes. It is about responsibilities, collaborations, and roles that mean something.</p><p>A role test is one way of making that meaning visible.</p><h2>References</h2><h3>Books</h3><ul><li><p>Rebecca Wirfs-Brock, Brian Wilkerson, and Lauren Wiener, <em>Designing Object-Oriented Software</em></p></li><li><p>Rebecca Wirfs-Brock and Alan McKean, <em>Object Design: Roles, Responsibilities, and Collaborations</em></p></li><li><p>Steve Freeman and Nat Pryce, <em>Growing Object-Oriented Software, Guided by Tests</em></p><p><a href="https://www.growing-object-oriented-software.com/">https://www.growing-object-oriented-software.com/</a></p></li></ul><h3>Papers and articles</h3><ul><li><p>Steve Freeman, Nat Pryce, Tim Mackinnon, and Joe Walnes, <em>Mock Roles, not Objects</em><br><a href="https://jmock.org/oopsla2004.pdf">https://jmock.org/oopsla2004.pdf</a></p></li><li><p>Manuel Rivero, <em>Role tests for implementation of interfaces discovered through TDD</em><br><a href="https://codesai.com/posts/2022/04/role-tests">https://codesai.com/posts/2022/04/role-tests</a></p></li><li><p>Manuel Rivero, <em>Example of role tests in Java with JUnit</em><br><a href="https://codesai.com/posts/2022/08/role-tests-junit">https://codesai.com/posts/2022/08/role-tests-junit</a></p></li><li><p>jMock, <em>Articles and Papers</em><br><a href="https://jmock.org/articles.html">https://jmock.org/articles.html</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[The work lanes that ate the team]]></title><description><![CDATA[Support, bugs, UX feedback, technical work and product development are not separate businesses. They are different kinds of demand entering the same value stream.]]></description><link>https://emmanuelvalverderamos.substack.com/p/the-work-lanes-that-ate-the-team</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/the-work-lanes-that-ate-the-team</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Mon, 22 Jun 2026 13:03:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FlxN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FlxN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FlxN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!FlxN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!FlxN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!FlxN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FlxN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1032700,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.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_!FlxN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!FlxN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!FlxN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!FlxN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67a82d68-d209-4dc4-bd0b-5a0d8938eb9a_1024x559.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Imagine a small restaurant with one kitchen. There is one group of cooks, one oven, one set of knives, one queue of orders, and one dining room full of people waiting. At first, the work is obvious: cook good food and serve it while it is still warm.</p><p>Then someone tries to make the kitchen more predictable. Forty percent of the kitchen is for new dishes. Twenty percent is for complaints. Fifteen percent is for cleaning. Fifteen percent is for menu improvements. Ten percent is for sharpening knives, repairing tools and keeping the kitchen healthy.</p><p>The spreadsheet looks mature because every category has space. Nobody can say their concern was ignored. The manager can point at the plan and say, &#8220;We have reserved capacity for everything.&#8221;</p><p>Then reality enters through the back door. A pan catches fire during the &#8220;new dishes&#8221; allocation. A knife becomes blunt during the &#8220;complaints&#8221; allocation. A customer sends food back during the &#8220;cleaning&#8221; allocation. The oven starts failing during the &#8220;menu improvement&#8221; allocation.</p><p>Nobody in the dining room cares which percentage bucket the problem belongs to. They care whether the restaurant can serve good food.</p><p>Software teams are not kitchens, but the mistake is similar. We split work into artificial lanes and manage the lanes as if they were independent sources of value: product development, support, bugs, UX feedback, technical initiatives, reliability, security, operations and documentation.</p><p>The names are useful. The separation is the trap.</p><p>There is only one business. There is one product investment. There is one value stream. The rest is demand classification.</p><h2>TL;DR</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OJDO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff61843a8-cb8d-40c7-81dd-e4b8c33681d9_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OJDO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff61843a8-cb8d-40c7-81dd-e4b8c33681d9_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!OJDO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff61843a8-cb8d-40c7-81dd-e4b8c33681d9_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!OJDO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff61843a8-cb8d-40c7-81dd-e4b8c33681d9_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!OJDO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff61843a8-cb8d-40c7-81dd-e4b8c33681d9_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OJDO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff61843a8-cb8d-40c7-81dd-e4b8c33681d9_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f61843a8-cb8d-40c7-81dd-e4b8c33681d9_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1768431,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff61843a8-cb8d-40c7-81dd-e4b8c33681d9_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OJDO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff61843a8-cb8d-40c7-81dd-e4b8c33681d9_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!OJDO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff61843a8-cb8d-40c7-81dd-e4b8c33681d9_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!OJDO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff61843a8-cb8d-40c7-81dd-e4b8c33681d9_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!OJDO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff61843a8-cb8d-40c7-81dd-e4b8c33681d9_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Many companies create &#8220;work lanes&#8221; and reserve fixed percentages of team capacity for each one: product work, support, bugs, UX feedback, technical initiatives, security, reliability and operations. This feels organised, but it often hides the real problem: the team has more demand than capacity and too much <em>Work in Progress</em> (WIP).</p><p>The flaw is not that support, bugs, UX feedback or technical work are unimportant. They are real. The flaw is treating them as separate internal businesses competing for fixed slices of the same team.</p><p>A product team has one economic mission: protect and improve the product&#8217;s ability to create value. Product work creates or extends user value. Support restores blocked value. Bugs remove negative value. UX feedback improves learning. Technical work protects delivery capacity, reduces risk or lowers the cost of change. Reliability protects continuity of value. Security reduces exposure to loss.</p><p>All of them belong to the same economic system.</p><p>When these categories become fixed percentages, the team starts serving the allocation model instead of the user. Flow gets worse, context switching grows, forecasts become fragile, and every delay becomes a debate about which lane consumed too much capacity.</p><p>A better model is one value stream with visible demand types, explicit decision policies, limited WIP, pull-based intake, clear expedite rules, enough slack for reality, and prioritisation based on value, risk, learning, cost of delay, cost of change and recovery needs.</p><p>Do not split the business into fictional internal lanes. Make the trade-offs visible and decide what protects or improves value next.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Table of contents</h2><ol><li><p>What companies call work lanes</p></li><li><p>The demand is real, the separation is imaginary</p></li><li><p>There is only one business</p></li><li><p>The wrong model: many lanes, one overloaded team</p></li><li><p>The alternative in one picture</p></li><li><p>Output, outcome and throughput</p></li><li><p>The resource utilization trap</p></li><li><p>The 50/50 person is not 50/50</p></li><li><p>WIP, queues and Little&#8217;s Law</p></li><li><p>The <em>Theory of Constraints</em> problem</p></li><li><p>Why this becomes anti-agile</p></li><li><p>What fixed percentages hide</p></li><li><p>What should happen to support, bugs and technical work</p></li><li><p>Explicit policies instead of capacity slices</p></li><li><p>A worked example: one value stream in practice</p></li><li><p>What to measure instead</p></li><li><p>The practical replacement</p></li><li><p>Conclusions</p></li><li><p>References</p></li></ol><h2>What companies call work lanes</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!06gC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1906b24d-8001-46fb-8a36-72879730f7e0_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!06gC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1906b24d-8001-46fb-8a36-72879730f7e0_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!06gC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1906b24d-8001-46fb-8a36-72879730f7e0_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!06gC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1906b24d-8001-46fb-8a36-72879730f7e0_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!06gC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1906b24d-8001-46fb-8a36-72879730f7e0_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!06gC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1906b24d-8001-46fb-8a36-72879730f7e0_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1906b24d-8001-46fb-8a36-72879730f7e0_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:971999,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1906b24d-8001-46fb-8a36-72879730f7e0_1024x559.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_!06gC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1906b24d-8001-46fb-8a36-72879730f7e0_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!06gC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1906b24d-8001-46fb-8a36-72879730f7e0_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!06gC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1906b24d-8001-46fb-8a36-72879730f7e0_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!06gC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1906b24d-8001-46fb-8a36-72879730f7e0_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The idea usually begins with a reasonable fear. The team is drowning in mixed demand. Product wants features. Support wants incidents handled. UX wants feedback applied. Engineering wants time to remove painful technical constraints. Operations wants reliability work. Security wants remediation. Stakeholders want dates.</p><p>Everyone has a valid concern, and nobody wants their concern to disappear under someone else&#8217;s urgency. So the organisation creates percentages.</p><p>Maybe the team gets 60 percent for product features, 20 percent for support, 10 percent for technical initiatives, and 10 percent for UX feedback. Or 50, 30, 20. Or one day per iteration for bugs. Or every Friday for technical debt.</p><p>The shape changes, but the logic is the same: divide capacity so every category has protected space.</p><p>This feels fair because every stakeholder receives a slice. It feels responsible because it says, &#8220;We know support matters.&#8221; It feels mature because it converts messy human tension into numbers. The spreadsheet becomes a peace treaty, but a peace treaty is not the same as flow.</p><p>The percentages do not do the hard work that the organisation quietly expects from them. They do not reduce demand, increase team capacity, remove dependencies, shrink work, make incidents arrive on schedule, make customer feedback wait politely for the UX lane, or make technical risk respect the calendar.</p><p>The model gives the organisation a feeling of control while the system underneath keeps behaving like a queue. That is the first trap: the categories are real, but the lines are fictional.</p><h2>The demand is real, the separation is imaginary</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!o0Jo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02334d74-4b3a-4a40-ba62-cfec7af68325_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!o0Jo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02334d74-4b3a-4a40-ba62-cfec7af68325_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!o0Jo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02334d74-4b3a-4a40-ba62-cfec7af68325_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!o0Jo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02334d74-4b3a-4a40-ba62-cfec7af68325_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!o0Jo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02334d74-4b3a-4a40-ba62-cfec7af68325_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!o0Jo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02334d74-4b3a-4a40-ba62-cfec7af68325_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/02334d74-4b3a-4a40-ba62-cfec7af68325_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:928341,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02334d74-4b3a-4a40-ba62-cfec7af68325_1024x559.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_!o0Jo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02334d74-4b3a-4a40-ba62-cfec7af68325_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!o0Jo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02334d74-4b3a-4a40-ba62-cfec7af68325_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!o0Jo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02334d74-4b3a-4a40-ba62-cfec7af68325_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!o0Jo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02334d74-4b3a-4a40-ba62-cfec7af68325_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Support is real. Bugs are real. UX feedback is real. Technical work is real. Reliability work is real. Security work is real. Product development is real. The fiction begins when we pretend these are separate lines of business.</p><p>A product team has one attention system, one cognitive budget, one delivery capability, one set of constraints and one flow of work. When the same people must serve five lanes, those lanes are not five parallel roads. They are five entrances into the same narrow bridge.</p><p>That distinction matters because otherwise the argument becomes too easy to misread. This article is not saying &#8220;ignore support&#8221;, &#8220;bugs do not matter&#8221;, &#8220;technical work is fake&#8221;, &#8220;UX feedback is a distraction&#8221;, &#8220;security and reliability can wait forever&#8221;, or &#8220;only feature work has value&#8221;.</p><p>The argument is sharper than that. These things should not be managed as independent missions competing for fixed percentages of the same team. They should be understood as different classes of demand entering one value stream.</p><p>A value stream is the path from a need, risk, problem or learning opportunity to delivered, restored or protected value. That value may appear as a new capability, a repaired production behaviour, reduced support pain, lower operational risk, faster learning, lower cost of change, or better user experience.</p><p>Naming demand is useful. Allocating the team into fixed percentages by demand type is the trap. Labels help us see the system. Percentages often make us serve the label instead of the user.</p><h2>There is only one business</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zP7q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F169f8ea0-cb0f-4d5f-b966-2ac1129886cb_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zP7q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F169f8ea0-cb0f-4d5f-b966-2ac1129886cb_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!zP7q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F169f8ea0-cb0f-4d5f-b966-2ac1129886cb_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!zP7q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F169f8ea0-cb0f-4d5f-b966-2ac1129886cb_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!zP7q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F169f8ea0-cb0f-4d5f-b966-2ac1129886cb_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zP7q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F169f8ea0-cb0f-4d5f-b966-2ac1129886cb_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/169f8ea0-cb0f-4d5f-b966-2ac1129886cb_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1182227,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F169f8ea0-cb0f-4d5f-b966-2ac1129886cb_1024x559.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_!zP7q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F169f8ea0-cb0f-4d5f-b966-2ac1129886cb_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!zP7q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F169f8ea0-cb0f-4d5f-b966-2ac1129886cb_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!zP7q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F169f8ea0-cb0f-4d5f-b966-2ac1129886cb_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!zP7q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F169f8ea0-cb0f-4d5f-b966-2ac1129886cb_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is where the usual conversation goes wrong. Many organisations still speak as if there were several businesses inside the company. &#8220;Business&#8221; wants features. &#8220;Product&#8221; wants outcomes. &#8220;Technology&#8221; wants technical health. &#8220;Support&#8221; wants issues solved. &#8220;UX&#8221; wants feedback addressed. &#8220;Security&#8221; wants risks removed.</p><p>This language feels harmless, but it quietly divides the company into tribes. Then every tribe asks for its lane.</p><p>But there is only one business.</p><p>Technology is not a supplier to the business. Technology is part of the business. Product is not a customer of engineering. Product and engineering are different responsibilities inside the same investment system. Support is not outside product. Support is product reality speaking back through pain. UX feedback is not a decorative stream. It is learning from the user&#8217;s contact with the product. Technical work is not a private engineering hobby. It protects future delivery capacity, risk, liquidity and cost of change.</p><p>A company does not succeed because one internal lane received its allocation. It succeeds when the product creates enough value to justify the money, time, attention and judgement invested in it.</p><p>So the central question is not, &#8220;How much percentage does each lane deserve?&#8221; The better question is, &#8220;What work best protects or improves user value now?&#8221;</p><p>That question is harder because it does not allow every stakeholder to hide behind a percentage. It forces the real trade-off into the open. Sometimes the answer is a feature that creates new value. Sometimes it is a production incident that must be resolved. Sometimes it is a confusing onboarding step that damages activation. Sometimes it is a refactor that lowers the cost of the next change. Sometimes it is a deletion that removes carrying cost. Sometimes it is a deployment improvement that reduces release risk. Sometimes it is a security fix that reduces exposure. Sometimes it is a discovery activity that prevents building the wrong thing.</p><p>The work type is not the point. The economic effect is the point.</p><h2>The wrong model: many lanes, one overloaded team</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gK6g!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5576e-0547-4f5b-832d-04fe9dbdfce3_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gK6g!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5576e-0547-4f5b-832d-04fe9dbdfce3_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!gK6g!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5576e-0547-4f5b-832d-04fe9dbdfce3_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!gK6g!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5576e-0547-4f5b-832d-04fe9dbdfce3_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!gK6g!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5576e-0547-4f5b-832d-04fe9dbdfce3_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gK6g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5576e-0547-4f5b-832d-04fe9dbdfce3_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aab5576e-0547-4f5b-832d-04fe9dbdfce3_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:737256,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5576e-0547-4f5b-832d-04fe9dbdfce3_1024x559.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_!gK6g!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5576e-0547-4f5b-832d-04fe9dbdfce3_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!gK6g!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5576e-0547-4f5b-832d-04fe9dbdfce3_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!gK6g!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5576e-0547-4f5b-832d-04fe9dbdfce3_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!gK6g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5576e-0547-4f5b-832d-04fe9dbdfce3_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>The fixed-lane model often looks like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GzHh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7888a2ef-8e26-4582-ab16-b0ccc5340c23_2955x764.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GzHh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7888a2ef-8e26-4582-ab16-b0ccc5340c23_2955x764.png 424w, https://substackcdn.com/image/fetch/$s_!GzHh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7888a2ef-8e26-4582-ab16-b0ccc5340c23_2955x764.png 848w, https://substackcdn.com/image/fetch/$s_!GzHh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7888a2ef-8e26-4582-ab16-b0ccc5340c23_2955x764.png 1272w, https://substackcdn.com/image/fetch/$s_!GzHh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7888a2ef-8e26-4582-ab16-b0ccc5340c23_2955x764.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GzHh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7888a2ef-8e26-4582-ab16-b0ccc5340c23_2955x764.png" width="1456" height="376" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7888a2ef-8e26-4582-ab16-b0ccc5340c23_2955x764.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:376,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131373,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7888a2ef-8e26-4582-ab16-b0ccc5340c23_2955x764.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_!GzHh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7888a2ef-8e26-4582-ab16-b0ccc5340c23_2955x764.png 424w, https://substackcdn.com/image/fetch/$s_!GzHh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7888a2ef-8e26-4582-ab16-b0ccc5340c23_2955x764.png 848w, https://substackcdn.com/image/fetch/$s_!GzHh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7888a2ef-8e26-4582-ab16-b0ccc5340c23_2955x764.png 1272w, https://substackcdn.com/image/fetch/$s_!GzHh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7888a2ef-8e26-4582-ab16-b0ccc5340c23_2955x764.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This model has a hidden contradiction. It says the team has several protected lines of work, but it does not give the team several independent capacities. The same people still need to think, coordinate, design, test, review, deploy, learn, monitor and support.</p><p>If the team had four independent kitchens, perhaps four percentages would make sense. But the team has one kitchen.</p><p>Every new lane creates a promise. Every promise creates pressure to start work. Every started item creates WIP. Every WIP item carries context, coordination and unfinished decisions. The team becomes busier, but value moves more slowly.</p><p>The board looks active, but the delivery system becomes heavy.</p><h2>The alternative in one picture</h2><p>The alternative is not chaos. It is one visible value stream with different demand types, explicit policies, limited WIP and pull-based intake.</p><p>This model does not deny that work is different. It denies that different work types should become separate internal businesses.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IS22!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb028c980-4a37-4870-8f58-edabff0737a4_3803x1388.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IS22!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb028c980-4a37-4870-8f58-edabff0737a4_3803x1388.png 424w, https://substackcdn.com/image/fetch/$s_!IS22!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb028c980-4a37-4870-8f58-edabff0737a4_3803x1388.png 848w, https://substackcdn.com/image/fetch/$s_!IS22!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb028c980-4a37-4870-8f58-edabff0737a4_3803x1388.png 1272w, https://substackcdn.com/image/fetch/$s_!IS22!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb028c980-4a37-4870-8f58-edabff0737a4_3803x1388.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IS22!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb028c980-4a37-4870-8f58-edabff0737a4_3803x1388.png" width="1456" height="531" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b028c980-4a37-4870-8f58-edabff0737a4_3803x1388.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:531,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:217575,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb028c980-4a37-4870-8f58-edabff0737a4_3803x1388.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_!IS22!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb028c980-4a37-4870-8f58-edabff0737a4_3803x1388.png 424w, https://substackcdn.com/image/fetch/$s_!IS22!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb028c980-4a37-4870-8f58-edabff0737a4_3803x1388.png 848w, https://substackcdn.com/image/fetch/$s_!IS22!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb028c980-4a37-4870-8f58-edabff0737a4_3803x1388.png 1272w, https://substackcdn.com/image/fetch/$s_!IS22!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb028c980-4a37-4870-8f58-edabff0737a4_3803x1388.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><a href="https://mermaid.live/edit#pako:eNpdUslymzAYfhWNzthT4g1zaKc2duLEznSyTBfhg4J-QFMhMUI4cTHvXhlKE8xJ3yqJXxWOFAPs41io1yil2qDtQ6hDiez3lWxkpDIuE8Qgo5Lt0WDwGS2qQEGBuEG5VgYig5RGPLPgAOhARQlf6nNFV7M4p0736oSWJFBIKoMKY3fa9w0_oTihgCwFLQoeH_9ticwxh_3HuqA5xIqsgZpSw77Pr8ljmefqQ3vLX5NFmVxwN-T5B4oB2AuNfl9oG_IEUSp5RAV6VfpSviUPIDh94YKb44V2Rx4hKnUndOKqEbfV6i0XPLJ_j0HEC64kypUljnVnXLfGDl734U0fbvrwtg_v3mFHbRtqR75vvg0Ez7gB1g7NDkUDzf5fZtcY78maS16kwBykQQAt2lWhxMEm7eQtpyWwJocdnGjOsG90CQ7OQGf0DHF17gyxSSGDEPt2ySCmpTAhDmVtYzmVv5TKuqRWZZJiP6aisKjMGTUQcJpo-m4ByUAvVSkN9t0rBwPjRuld-56bZ93UYr_Cb9gfXbnD8XjkTqbuyJuM5zZwxP50PvS8-XwynnmzkTvzprWD_zTn-DT0ZpP6L6Rf9vk">Playground</a><code>
</code></figcaption></figure></div><p>The important move is not the diagram itself. The important move is changing the management question.</p><p>The old model asks, &#8220;Which lane owns capacity?&#8221; The better model asks, &#8220;Which demand should enter the constrained delivery system next, and why?&#8221;</p><p>That change turns the conversation from allocation politics into economic judgement.</p><h2>Output, outcome and throughput</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZYMD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac2bedb5-0686-4123-8a6b-f3ff617d1547_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZYMD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac2bedb5-0686-4123-8a6b-f3ff617d1547_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ZYMD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac2bedb5-0686-4123-8a6b-f3ff617d1547_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ZYMD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac2bedb5-0686-4123-8a6b-f3ff617d1547_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ZYMD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac2bedb5-0686-4123-8a6b-f3ff617d1547_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZYMD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac2bedb5-0686-4123-8a6b-f3ff617d1547_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ac2bedb5-0686-4123-8a6b-f3ff617d1547_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:503002,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac2bedb5-0686-4123-8a6b-f3ff617d1547_1024x559.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_!ZYMD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac2bedb5-0686-4123-8a6b-f3ff617d1547_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ZYMD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac2bedb5-0686-4123-8a6b-f3ff617d1547_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ZYMD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac2bedb5-0686-4123-8a6b-f3ff617d1547_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ZYMD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac2bedb5-0686-4123-8a6b-f3ff617d1547_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Work lanes usually optimise output accounting. They ask whether each category received activity, whether each internal group was represented in the iteration, and whether the promised capacity distribution was respected.</p><p>Those questions may sound responsible, but they are not enough. They mostly measure whether a category was fed.</p><p>A product organisation needs a better distinction:</p><ul><li><p><strong>Output</strong> is what the team ships</p></li><li><p><strong>Outcome</strong> is what changes because of the work</p></li><li><p><strong>Throughput</strong> is the rate at which the system finishes work over time<br>Fixed lanes blur these ideas. They turn output per category into a proxy for value. If the team delivered five support tickets, two technical tasks and three feature stories, the spreadsheet looks balanced. But that does not tell us whether users are better served, whether support pain went down, whether the next change became cheaper, whether a risk was actually reduced, or whether the delivery system is healthier.</p></li></ul><p>A lane can receive its percentage and still create little value. A team can be active in every category and still produce poor outcomes. A team can increase output and still reduce throughput by fragmenting its attention.</p><p>This is why lane accounting is seductive. It gives the organisation visible evidence of activity. But activity is not value. Motion is not progress. A full board is not a healthy system.</p><p>The business does not benefit because a lane was busy. It benefits when outcomes improve and the delivery system remains healthy enough to keep learning. Without output, nothing reaches users. Without throughput, delivery becomes sluggish and learning slows down. Without outcome, software may be shipped, but value remains unproven.</p><p>Work lanes become dangerous when they reward output by category, obscure throughput across the system and barely examine outcome.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;bba37f4c-87fa-4b2c-861b-17eabb231b5f&quot;,&quot;caption&quot;:&quot;&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Output, Outcome, and Throughput: What Product Teams are really measuring when they say they deliver value&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-05-12T05:01:35.830Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/vimeo/w_728,c_limit,d_video_placeholder.png/206617354&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/output-outcome-and-throughput-what&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:189660243,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>The resource utilization trap</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TA81!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c62e9fe-5774-4499-98a9-362ea7b54e0b_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TA81!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c62e9fe-5774-4499-98a9-362ea7b54e0b_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!TA81!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c62e9fe-5774-4499-98a9-362ea7b54e0b_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!TA81!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c62e9fe-5774-4499-98a9-362ea7b54e0b_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!TA81!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c62e9fe-5774-4499-98a9-362ea7b54e0b_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TA81!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c62e9fe-5774-4499-98a9-362ea7b54e0b_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6c62e9fe-5774-4499-98a9-362ea7b54e0b_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:790827,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c62e9fe-5774-4499-98a9-362ea7b54e0b_1024x559.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_!TA81!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c62e9fe-5774-4499-98a9-362ea7b54e0b_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!TA81!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c62e9fe-5774-4499-98a9-362ea7b54e0b_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!TA81!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c62e9fe-5774-4499-98a9-362ea7b54e0b_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!TA81!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c62e9fe-5774-4499-98a9-362ea7b54e0b_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Fixed percentages are also a form of the resource utilization trap. They make the organisation feel responsible because every slice of capacity has a job. Nobody is idle. Nothing appears wasted. Every lane has a plan. Every person is attached to something. The calendar looks full.</p><p>But a fully allocated team is not necessarily a productive team. A fully occupied road is not an efficient road. It is a traffic jam.</p><p>The problem is not utilization itself. Capacity matters. Waste matters. People should not be left unsupported, bored or disconnected from meaningful work. The problem begins when utilization becomes the primary target and flow becomes secondary.</p><p>When the system is optimised to keep everyone busy, a predictable chain appears:</p><ol><li><p>Work gets pushed in faster than it can move out</p></li><li><p>More work starts</p></li><li><p>Less work finishes</p></li><li><p>Coordination grows</p></li><li><p>Waiting grows</p></li><li><p>Re-planning grows</p></li><li><p>Status reporting grows</p></li><li><p>Context switching grows</p></li><li><p>The team becomes a machine for converting effort into delay<br>That loop looks like this:</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uY28!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc3a760e-e3bb-4714-a82b-9e49a52def6f_717x1948.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uY28!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc3a760e-e3bb-4714-a82b-9e49a52def6f_717x1948.png 424w, https://substackcdn.com/image/fetch/$s_!uY28!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc3a760e-e3bb-4714-a82b-9e49a52def6f_717x1948.png 848w, https://substackcdn.com/image/fetch/$s_!uY28!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc3a760e-e3bb-4714-a82b-9e49a52def6f_717x1948.png 1272w, https://substackcdn.com/image/fetch/$s_!uY28!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc3a760e-e3bb-4714-a82b-9e49a52def6f_717x1948.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uY28!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc3a760e-e3bb-4714-a82b-9e49a52def6f_717x1948.png" width="717" height="1948" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bc3a760e-e3bb-4714-a82b-9e49a52def6f_717x1948.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1948,&quot;width&quot;:717,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:99952,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc3a760e-e3bb-4714-a82b-9e49a52def6f_717x1948.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_!uY28!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc3a760e-e3bb-4714-a82b-9e49a52def6f_717x1948.png 424w, https://substackcdn.com/image/fetch/$s_!uY28!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc3a760e-e3bb-4714-a82b-9e49a52def6f_717x1948.png 848w, https://substackcdn.com/image/fetch/$s_!uY28!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc3a760e-e3bb-4714-a82b-9e49a52def6f_717x1948.png 1272w, https://substackcdn.com/image/fetch/$s_!uY28!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc3a760e-e3bb-4714-a82b-9e49a52def6f_717x1948.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is why some slack is not waste. In knowledge work, slack is often the space that allows flow, learning, helping, incident response, refactoring, mentoring, recovery and better decisions.</p><p>The absence of slack does not prove discipline. It may prove the system has no room to breathe.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;3d85de8d-bebc-48cb-8433-8295b77ebea4&quot;,&quot;caption&quot;:&quot;Modern organizations often praise busyness as if it were the clearest sign of effectiveness. A team with no idle time appears disciplined, efficient, and productive. A room full of people constantly moving, answering, processing, and multitasking seems, at first glance, like a healthy system. Yet the core argument of&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;The Resource Utilization Trap: Why Keeping Everyone Busy Can Destroy Real Productivity&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-05-19T05:00:58.445Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!XHGN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/the-resource-utilization-trap-why&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:189661244,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>The 50/50 person is not 50/50</h2><p>The human version of the same mistake is the person split between two teams. On paper, the person is 50 percent in Team A and 50 percent in Team B. It looks efficient. Both teams get help. The person is shared. Capacity is maximised.</p><p>In reality, Team A keeps moving while the person is with Team B. Decisions are made. Code changes. Product context evolves. Small conversations happen. Assumptions accumulate. When the person returns to Team A, they are not simply &#8220;back&#8221;. They must rebuild the mental model they missed.</p><p>Then the same thing happens in Team B.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-jwh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83247624-55cc-40f5-88ce-319a2556024b_1818x1134.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-jwh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83247624-55cc-40f5-88ce-319a2556024b_1818x1134.png 424w, https://substackcdn.com/image/fetch/$s_!-jwh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83247624-55cc-40f5-88ce-319a2556024b_1818x1134.png 848w, https://substackcdn.com/image/fetch/$s_!-jwh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83247624-55cc-40f5-88ce-319a2556024b_1818x1134.png 1272w, https://substackcdn.com/image/fetch/$s_!-jwh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83247624-55cc-40f5-88ce-319a2556024b_1818x1134.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-jwh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83247624-55cc-40f5-88ce-319a2556024b_1818x1134.png" width="1456" height="908" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/83247624-55cc-40f5-88ce-319a2556024b_1818x1134.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:908,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:107583,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83247624-55cc-40f5-88ce-319a2556024b_1818x1134.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_!-jwh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83247624-55cc-40f5-88ce-319a2556024b_1818x1134.png 424w, https://substackcdn.com/image/fetch/$s_!-jwh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83247624-55cc-40f5-88ce-319a2556024b_1818x1134.png 848w, https://substackcdn.com/image/fetch/$s_!-jwh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83247624-55cc-40f5-88ce-319a2556024b_1818x1134.png 1272w, https://substackcdn.com/image/fetch/$s_!-jwh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83247624-55cc-40f5-88ce-319a2556024b_1818x1134.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The person is not doing half of each team&#8217;s work. They are also paying the re-entry cost for both teams. Some of that cost is visible in meetings. Much of it is invisible: rereading discussions, asking questions that were answered yesterday, hesitating before changing code, holding two product narratives in memory, feeling guilty in both places, and missing the small context that never reaches formal documentation.</p><p>The 150 percent figure is a useful metaphor, not a universal measurement. The exact cost depends on the work, the teams, the domain and the interruption pattern. But the direction is real: splitting attention does not split cleanly. Cognitive work has setup cost, re-entry cost and coordination cost.</p><p>The same thing happens when one team is split across several work lanes. The team is supposedly 60 percent product, 20 percent support, 10 percent technical work and 10 percent UX. But each lane carries its own context, stakeholders, urgency, vocabulary, artefacts and unfinished stories.</p><p>A spreadsheet can divide capacity. It cannot divide attention without loss.</p><h2>WIP, queues and Little&#8217;s Law</h2><p>There is a simple reason these systems become painful: queues grow when demand exceeds effective capacity.</p><p>In software, queues are often invisible. They can appear as a half-finished feature, a bug waiting for someone to reload context, a support issue waiting for product clarification, a technical decision waiting for architecture review, a pull request waiting for review, a release waiting for approval, a stakeholder waiting for an answer, a test suite waiting for someone to understand why it is flaky, or a dependency waiting for another team.</p><p>When a team has too much WIP, every item spends more time waiting. People may still be busy, but value is not moving.</p><p>Little&#8217;s Law gives us a simple relationship:</p><div class="callout-block" data-callout="true"><p style="text-align: center;"><strong>Average lead time = Average WIP / Average throughput</strong></p></div><p>This is not a magic formula for prediction. It is a useful way to think about flow. If WIP grows and throughput does not grow with it, lead time gets worse.</p><p>That is exactly what fixed work lanes often do. Product wants visible progress. Support wants visible progress. UX wants visible progress. Technical initiatives want visible progress. Security wants visible progress. Reliability wants visible progress. So everything starts.</p><p>Then everything waits.</p><p>The organisation sees slow delivery and asks for more planning. More planning consumes more attention. More attention spent on planning reduces the attention available for finishing. The system becomes slower and more managed at the same time.</p><p>This is the deeper failure: the team is not short of categories. It is short of flow.</p><h2>The <em>Theory of Constraints</em> problem</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xMiT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f22109e-b385-4304-a3bd-7110ccd1f708_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xMiT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f22109e-b385-4304-a3bd-7110ccd1f708_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!xMiT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f22109e-b385-4304-a3bd-7110ccd1f708_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!xMiT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f22109e-b385-4304-a3bd-7110ccd1f708_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!xMiT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f22109e-b385-4304-a3bd-7110ccd1f708_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xMiT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f22109e-b385-4304-a3bd-7110ccd1f708_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f22109e-b385-4304-a3bd-7110ccd1f708_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:592351,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f22109e-b385-4304-a3bd-7110ccd1f708_1024x559.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_!xMiT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f22109e-b385-4304-a3bd-7110ccd1f708_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!xMiT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f22109e-b385-4304-a3bd-7110ccd1f708_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!xMiT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f22109e-b385-4304-a3bd-7110ccd1f708_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!xMiT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f22109e-b385-4304-a3bd-7110ccd1f708_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Theory of Constraints</em> (TOC) gives another way to see the same issue. A system&#8217;s throughput is limited by its constraint. If the constraint is overloaded, optimising everything else does not improve the system. It can make the system worse by producing more work than the constraint can handle.</p><p>Fixed lanes often create local optimisation. Product optimises its lane. Support optimises its lane. UX optimises its lane. Technology optimises its lane. Security optimises its lane. Every lane can make a reasonable local argument, but the whole system slows down because the same team is the constraint.</p><p>The problem is not that any stakeholder is wrong. The problem is that the system is being managed as if every demand type had its own independent capacity.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3iYF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71325d8c-7e89-4fcd-b9a9-9cb304e4bdb9_2290x992.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3iYF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71325d8c-7e89-4fcd-b9a9-9cb304e4bdb9_2290x992.png 424w, https://substackcdn.com/image/fetch/$s_!3iYF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71325d8c-7e89-4fcd-b9a9-9cb304e4bdb9_2290x992.png 848w, https://substackcdn.com/image/fetch/$s_!3iYF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71325d8c-7e89-4fcd-b9a9-9cb304e4bdb9_2290x992.png 1272w, https://substackcdn.com/image/fetch/$s_!3iYF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71325d8c-7e89-4fcd-b9a9-9cb304e4bdb9_2290x992.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3iYF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71325d8c-7e89-4fcd-b9a9-9cb304e4bdb9_2290x992.png" width="1456" height="631" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/71325d8c-7e89-4fcd-b9a9-9cb304e4bdb9_2290x992.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:631,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:141761,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71325d8c-7e89-4fcd-b9a9-9cb304e4bdb9_2290x992.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_!3iYF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71325d8c-7e89-4fcd-b9a9-9cb304e4bdb9_2290x992.png 424w, https://substackcdn.com/image/fetch/$s_!3iYF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71325d8c-7e89-4fcd-b9a9-9cb304e4bdb9_2290x992.png 848w, https://substackcdn.com/image/fetch/$s_!3iYF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71325d8c-7e89-4fcd-b9a9-9cb304e4bdb9_2290x992.png 1272w, https://substackcdn.com/image/fetch/$s_!3iYF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71325d8c-7e89-4fcd-b9a9-9cb304e4bdb9_2290x992.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In TOC terms, the sensible response is to subordinate the system to the constraint. If the team&#8217;s attention is the constraint, do not feed it five parallel lanes and call that balance. Protect the constraint. Reduce unnecessary WIP. Make priorities explicit. Remove interruptions where possible. Improve the constraint if needed. Do not allow every non-constraint stakeholder to push work into the system just because their lane has a percentage.</p><p>That is not discipline. That is congestion with a budget.</p><h2>Why this becomes anti-agile</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VkfC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9399882f-3f99-4ba3-b51c-27056fc45885_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VkfC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9399882f-3f99-4ba3-b51c-27056fc45885_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!VkfC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9399882f-3f99-4ba3-b51c-27056fc45885_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!VkfC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9399882f-3f99-4ba3-b51c-27056fc45885_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!VkfC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9399882f-3f99-4ba3-b51c-27056fc45885_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VkfC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9399882f-3f99-4ba3-b51c-27056fc45885_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9399882f-3f99-4ba3-b51c-27056fc45885_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1060747,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9399882f-3f99-4ba3-b51c-27056fc45885_1024x559.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_!VkfC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9399882f-3f99-4ba3-b51c-27056fc45885_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!VkfC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9399882f-3f99-4ba3-b51c-27056fc45885_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!VkfC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9399882f-3f99-4ba3-b51c-27056fc45885_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!VkfC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9399882f-3f99-4ba3-b51c-27056fc45885_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A fixed allocation model may look Agile from a distance. It may live inside iterations. It may use a board. It may have refinement, planning, stand-ups and retrospectives. It may call the work &#8220;stories&#8221;. It may even happen inside a team that sincerely believes it is working in an adaptive way.</p><p>But Agile is not the presence of ceremonies. Agile is a way of organising work around value, feedback, collaboration, technical excellence, simplicity and adaptation.</p><p>Fixed work-lane percentages become anti-agile when they replace adaptation with allocation. The problem is not that a team classifies demand. The problem is that the team follows a pre-negotiated capacity split instead of responding to the next most valuable, risky or time-sensitive piece of work.</p><p>That conflict appears in several ways:</p><ul><li><p>It conflicts with customer value because it asks whether a lane received capacity before asking whether the next piece of work is the most valuable thing to do</p></li><li><p>It conflicts with responding to change because it freezes a capacity split before reality has finished speaking</p></li><li><p>It conflicts with sustainable pace because every surprise must either break the allocation, break the plan or break the people</p></li><li><p>It conflicts with technical excellence when technical work is treated as a side allowance rather than part of the team&#8217;s ability to respond</p></li><li><p>It conflicts with simplicity because it institutionalises more categories, more negotiation, more partial commitments and more coordination</p></li><li><p>It conflicts with self-organisation because the team is not truly deciding how best to achieve the goal. It is operating inside a capacity budget carved by internal categories<br>If an iteration contains four unrelated mini-roadmaps because each lane needs its percentage, the team may be busy, but it is not focused. An iteration is not a drawer for unrelated obligations. It should have a direction, and a product team should have a mission.</p></li></ul><p>That mission is not &#8220;satisfy every internal bucket&#8221;. It is &#8220;protect and improve the product&#8217;s ability to create value&#8221;.</p><h2>What fixed percentages hide</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZY34!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa31be63c-13bd-4f38-9272-deb97b02a509_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZY34!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa31be63c-13bd-4f38-9272-deb97b02a509_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ZY34!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa31be63c-13bd-4f38-9272-deb97b02a509_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ZY34!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa31be63c-13bd-4f38-9272-deb97b02a509_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ZY34!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa31be63c-13bd-4f38-9272-deb97b02a509_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZY34!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa31be63c-13bd-4f38-9272-deb97b02a509_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a31be63c-13bd-4f38-9272-deb97b02a509_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:781063,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa31be63c-13bd-4f38-9272-deb97b02a509_1024x559.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_!ZY34!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa31be63c-13bd-4f38-9272-deb97b02a509_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ZY34!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa31be63c-13bd-4f38-9272-deb97b02a509_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ZY34!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa31be63c-13bd-4f38-9272-deb97b02a509_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ZY34!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa31be63c-13bd-4f38-9272-deb97b02a509_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Fixed percentages feel transparent, but they often hide the most important information.</p><p>They hide <strong>overload</strong>. If every lane has capacity assigned, the system can look planned even when total demand is far beyond what the team can absorb.</p><p>They hide <strong>opportunity cost</strong>. A technical initiative started too early can block a support problem that is damaging trust. A feature started because the product lane needed movement can block a reliability fix that would reduce incidents. A UX item started because the UX lane has allocation can block a smaller product experiment that would teach more.</p><p>They hide <strong>cost of delay</strong>. Some work loses value when delayed. Some risks become more expensive with time. Some support issues compound through repeated contacts. Some technical constraints grow more expensive because every new change pays the tax.</p><p>They hide <strong>cost of carry</strong>. Every unfinished item must be remembered, discussed, updated, re-estimated, re-explained and eventually reloaded into someone&#8217;s head.</p><p>They hide <strong>basal cost</strong>. Every feature that exists must be supported, understood, monitored, adapted, tested and carried through future change. More software is not only more capability. It is also more surface area.</p><p>They hide <strong>the real constraint</strong>. Instead of asking, &#8220;What is limiting our ability to deliver value?&#8221; the organisation asks, &#8220;Did each lane get its share?&#8221;</p><p>That is a weaker question, and weaker questions produce weaker decisions.</p><h2>What should happen to support, bugs and technical work</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!H-DO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4644617f-a219-4132-9b49-0f4421c673de_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!H-DO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4644617f-a219-4132-9b49-0f4421c673de_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!H-DO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4644617f-a219-4132-9b49-0f4421c673de_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!H-DO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4644617f-a219-4132-9b49-0f4421c673de_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!H-DO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4644617f-a219-4132-9b49-0f4421c673de_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!H-DO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4644617f-a219-4132-9b49-0f4421c673de_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4644617f-a219-4132-9b49-0f4421c673de_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:874119,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4644617f-a219-4132-9b49-0f4421c673de_1024x559.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_!H-DO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4644617f-a219-4132-9b49-0f4421c673de_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!H-DO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4644617f-a219-4132-9b49-0f4421c673de_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!H-DO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4644617f-a219-4132-9b49-0f4421c673de_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!H-DO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4644617f-a219-4132-9b49-0f4421c673de_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>One value stream does not mean feature-only work. That would be a serious mistake.</p><p>Support matters because users are already paying the cost of a product failure, misunderstanding or operational gap. Bugs matter because they damage trust and produce rework. UX feedback matters because it improves the product&#8217;s contact with reality. Technical work matters because software has a carrying cost. Reliability matters because value that cannot survive production is not really delivered. Security matters because a product that creates avoidable exposure is not protecting the investment.</p><p>The correct move is not to remove these work types. The correct move is to stop treating them as separate businesses.</p><p>Here is a better classification:</p><p>Demand type What it really means Economic purpose Product feature A new or changed capability Create or extend user value Support A user or customer needs help Restore blocked or degraded value Bug Reality violates expected behaviour Remove negative value and protect trust UX feedback Users reveal friction or confusion Improve learning and reduce product friction Technical work The system is too costly, risky or slow to change Protect delivery capacity and reduce cost of change Reliability Production value is fragile Protect continuity of value Security The product has exposure to loss Reduce risk and protect the investment Documentation and knowledge work The system is hard to understand or operate Reduce cognitive load and coordination cost</p><p>This table does not create lanes. It creates language.</p><p>The difference is important. A lane says, &#8220;This category owns 20 percent.&#8221; A language says, &#8220;This kind of demand has this economic effect. Now let us decide whether it matters most.&#8221;</p><p>Production support deserves special care here. A product in production is not in a temporary exception state. It is alive. It will need support, learning, change, refinement and sometimes retirement. A team should measure the impact of production support on its development capacity, rotate exposure wisely when it helps learning, and turn repeated support themes into product improvement. Hiding production work in a percentage bucket misses the deeper lesson: support is one of the places where the product tells the truth.</p><h2>Explicit policies instead of capacity slices</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_r3C!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc013c2f0-b916-4670-9c13-d0f3c969ee8f_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_r3C!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc013c2f0-b916-4670-9c13-d0f3c969ee8f_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!_r3C!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc013c2f0-b916-4670-9c13-d0f3c969ee8f_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!_r3C!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc013c2f0-b916-4670-9c13-d0f3c969ee8f_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!_r3C!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc013c2f0-b916-4670-9c13-d0f3c969ee8f_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_r3C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc013c2f0-b916-4670-9c13-d0f3c969ee8f_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c013c2f0-b916-4670-9c13-d0f3c969ee8f_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:880404,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc013c2f0-b916-4670-9c13-d0f3c969ee8f_1024x559.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_!_r3C!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc013c2f0-b916-4670-9c13-d0f3c969ee8f_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!_r3C!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc013c2f0-b916-4670-9c13-d0f3c969ee8f_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!_r3C!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc013c2f0-b916-4670-9c13-d0f3c969ee8f_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!_r3C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc013c2f0-b916-4670-9c13-d0f3c969ee8f_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>One value stream needs decision policies. Without them, &#8220;one stream&#8221; can become a polite name for whoever shouts loudest.</p><p>Explicit policies answer the question that fixed percentages try to avoid: what should happen when demand collides?</p><p>A practical starting point could look like this:</p><p>Policy area Example policy Expedite Only user-blocking incidents, revenue-impacting issues, severe production risk or urgent compliance exposure can bypass normal order Support Repeated support themes must create product learning, not only ticket closure Bugs Prioritise by severity, reach, frequency, trust damage and recovery cost UX feedback Do not convert every comment into work. Look for repeated friction, high-impact journeys and evidence of outcome risk Technical work Prioritise when it reduces cost of change for imminent work, removes a recurring bottleneck or reduces meaningful operational risk Reliability Prioritise when failure impact, recovery time or operational toil threatens value continuity Security Integrate security into daily work where possible. Avoid treating it only as a downstream gate WIP Do not start new work while active work exceeds the agreed limit Slack Preserve capacity for incidents, helping, improvement and unexpected learning Abandonment Stop work when evidence shows the expected economic effect no longer justifies the cost</p><p>These policies should not be carved in stone. They should be sparse, visible, understandable and changeable by the people who use them. Their purpose is not to remove judgement. Their purpose is to make judgement explicit enough to improve.</p><p>A WIP limit works in the same way. It is not magic by itself. It matters when it exposes overload, forces conversations, encourages collaboration and helps the team stop starting so it can start finishing.</p><h2>A worked example: one value stream in practice</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rBZV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F218516b8-f620-4269-8638-405550b95358_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rBZV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F218516b8-f620-4269-8638-405550b95358_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!rBZV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F218516b8-f620-4269-8638-405550b95358_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!rBZV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F218516b8-f620-4269-8638-405550b95358_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!rBZV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F218516b8-f620-4269-8638-405550b95358_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rBZV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F218516b8-f620-4269-8638-405550b95358_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/218516b8-f620-4269-8638-405550b95358_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:455790,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F218516b8-f620-4269-8638-405550b95358_1024x559.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_!rBZV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F218516b8-f620-4269-8638-405550b95358_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!rBZV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F218516b8-f620-4269-8638-405550b95358_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!rBZV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F218516b8-f620-4269-8638-405550b95358_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!rBZV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F218516b8-f620-4269-8638-405550b95358_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Imagine a team entering an iteration with four candidates competing for attention.</p><p>The first candidate is an activation feature. Product believes it may help trial users reach first value faster. The second is a repeated support issue: customers are opening tickets because invoice exports sometimes fail when a rare tax configuration is present. The third is UX feedback showing confusion in onboarding, but the evidence is still anecdotal. The fourth is a refactor in the billing module that engineering says will reduce risk before changing pricing rules next month.</p><p>Under the lane model, each item fights for its percentage. Product argues that the feature belongs to the product lane. Support argues that the invoice issue belongs to the support lane. UX argues that feedback must be represented. Engineering argues that the technical initiative lane exists precisely for the refactor.</p><p>The discussion becomes a negotiation between buckets.</p><p>Under the value-stream model, the team asks a different question: which work best protects or improves value now?</p><p>Candidate Demand type Economic effect Decision Invoice export issue Support and bug Restores trust, reduces repeated support load, protects a revenue-adjacent workflow Do first Billing refactor Technical work Reduces cost and risk of the next pricing change Pair with the invoice fix where the touched area overlaps Activation feature Product feature Plausible value, but not urgent enough to start as a large batch Slice smaller and prepare the smallest valuable experiment Onboarding UX feedback Learning Signals possible friction, but evidence is still weak Convert into discovery work before implementation</p><p>Notice what happened. The team did not ignore Product. It did not ignore Support. It did not ignore UX. It did not ignore Technology. It refused to treat them as separate businesses.</p><p>The invoice issue goes first because users are already experiencing harm, support demand is repeating, and the affected workflow is important. The billing refactor is not accepted as a vague technical improvement. It is connected to imminent work and to the area already being touched, which improves its economic case. The activation feature is not rejected, but it is sliced because the team does not want to create a large batch while the system is already carrying urgent demand. The UX feedback is respected as learning, but not blindly converted into implementation.</p><p>This is what one value stream makes possible: not fewer conversations, but better conversations.</p><p>A useful decision map could look like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ucIr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2046f438-4433-4a3d-94a5-adf78febf0d0_1000x1000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ucIr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2046f438-4433-4a3d-94a5-adf78febf0d0_1000x1000.png 424w, https://substackcdn.com/image/fetch/$s_!ucIr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2046f438-4433-4a3d-94a5-adf78febf0d0_1000x1000.png 848w, https://substackcdn.com/image/fetch/$s_!ucIr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2046f438-4433-4a3d-94a5-adf78febf0d0_1000x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!ucIr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2046f438-4433-4a3d-94a5-adf78febf0d0_1000x1000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ucIr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2046f438-4433-4a3d-94a5-adf78febf0d0_1000x1000.png" width="1000" height="1000" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2046f438-4433-4a3d-94a5-adf78febf0d0_1000x1000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1000,&quot;width&quot;:1000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:101943,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2046f438-4433-4a3d-94a5-adf78febf0d0_1000x1000.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_!ucIr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2046f438-4433-4a3d-94a5-adf78febf0d0_1000x1000.png 424w, https://substackcdn.com/image/fetch/$s_!ucIr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2046f438-4433-4a3d-94a5-adf78febf0d0_1000x1000.png 848w, https://substackcdn.com/image/fetch/$s_!ucIr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2046f438-4433-4a3d-94a5-adf78febf0d0_1000x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!ucIr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2046f438-4433-4a3d-94a5-adf78febf0d0_1000x1000.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This diagram is intentionally not a Venn diagram. A Venn diagram can be useful to show overlap, but here it risks reinforcing the wrong mental model: Product, Support, UX and Technology as separate circles that occasionally intersect. The article&#8217;s thesis is stronger with flow and decision diagrams because the point is not overlap. The point is movement through one constrained system.</p><h2>What to measure instead</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YZ0i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4758613-d1ca-47f0-8e46-e21d5a6180a6_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YZ0i!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4758613-d1ca-47f0-8e46-e21d5a6180a6_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!YZ0i!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4758613-d1ca-47f0-8e46-e21d5a6180a6_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!YZ0i!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4758613-d1ca-47f0-8e46-e21d5a6180a6_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!YZ0i!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4758613-d1ca-47f0-8e46-e21d5a6180a6_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YZ0i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4758613-d1ca-47f0-8e46-e21d5a6180a6_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4758613-d1ca-47f0-8e46-e21d5a6180a6_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:946123,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4758613-d1ca-47f0-8e46-e21d5a6180a6_1024x559.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_!YZ0i!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4758613-d1ca-47f0-8e46-e21d5a6180a6_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!YZ0i!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4758613-d1ca-47f0-8e46-e21d5a6180a6_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!YZ0i!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4758613-d1ca-47f0-8e46-e21d5a6180a6_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!YZ0i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4758613-d1ca-47f0-8e46-e21d5a6180a6_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If fixed work-lane percentages are weak measures, what should we look at?</p><p>Measure flow, outcomes, quality, risk and learning. Useful signals include lead time from request to value delivered, cycle time from start to finish, throughput of completed valuable items, WIP by state and demand type, age of active work, percentage of unplanned work over time, escaped defects, repeated support themes, operational toil, user-impacting incidents, time from customer feedback to product learning, cost of delay for major waiting items, amount of work abandoned after learning, team health, focus and cognitive load.</p><p>For software delivery specifically, the current DORA metrics are useful because they place throughput and instability in tension. The five metrics are:</p><ul><li><p>Change lead time</p></li><li><p>Deployment frequency</p></li><li><p>Failed deployment recovery time</p></li><li><p>Change fail rate</p></li><li><p>Deployment rework rate<br>These metrics matter because they discourage the false choice between speed and stability. A team that moves fast but creates instability is not protecting value. A team that is stable only because it rarely changes is not necessarily healthy either.</p></li></ul><p>The same warning applies to any measurement system: do not isolate metrics into silos. If Product owns outcome, Engineering owns throughput, Operations owns recovery and Support owns tickets, the organisation has recreated work lanes through measurement. Shared ownership matters because the product is one system.</p><p>These measures are not perfect. They can be gamed if used badly. But they point closer to the real system than &#8220;percentage of capacity reserved per lane&#8221;.</p><p>The best use of measurement is not control. It is learning. A team does not need a dashboard to be watched harder. It needs visibility to make better decisions.</p><h2>The practical replacement</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Cpqx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1d0907f-01fb-423f-a35d-09ec2584c338_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Cpqx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1d0907f-01fb-423f-a35d-09ec2584c338_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Cpqx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1d0907f-01fb-423f-a35d-09ec2584c338_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Cpqx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1d0907f-01fb-423f-a35d-09ec2584c338_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Cpqx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1d0907f-01fb-423f-a35d-09ec2584c338_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Cpqx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1d0907f-01fb-423f-a35d-09ec2584c338_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c1d0907f-01fb-423f-a35d-09ec2584c338_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:486206,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1d0907f-01fb-423f-a35d-09ec2584c338_1024x559.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_!Cpqx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1d0907f-01fb-423f-a35d-09ec2584c338_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Cpqx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1d0907f-01fb-423f-a35d-09ec2584c338_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Cpqx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1d0907f-01fb-423f-a35d-09ec2584c338_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Cpqx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1d0907f-01fb-423f-a35d-09ec2584c338_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A team that wants to move away from work lanes can start with a small operating model.</p><p>First, collect all demand in one visible place. Do not hide support, bugs, UX feedback or technical work in separate tools where the team cannot see the whole system.</p><p>Second, classify demand by type, but do not allocate fixed percentages. The label exists to understand the nature of the demand, not to create a mini-budget.</p><p>Third, define explicit policies. Decide what qualifies as expedite work, how support themes become product learning, when technical work is justified, and how security and reliability work enter the stream.</p><p>Fourth, limit WIP. Make the limit uncomfortable enough to force conversations. A WIP limit that never changes behaviour is decoration.</p><p>Fifth, use pull. Start work when the system has capacity, not because a lane needs activity.</p><p>Sixth, preserve slack. If every person and every day are already allocated, the team has no room for incidents, helping, improvement, learning or recovery.</p><p>Seventh, review outcomes and flow together. Ask both: did the work help, and did the system remain healthy enough to keep learning?</p><p>Eighth, improve the system. If support keeps eating capacity, do not merely reserve more support capacity. Ask why support demand is so high. If technical work keeps interrupting features, ask which technical constraints are causing repeated delay. If UX feedback keeps arriving late, ask why discovery and delivery are disconnected.</p><p>The point is not to make the board prettier. The point is to make reality harder to ignore.</p><h2>Conclusions</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4BvL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef196166-4e1f-449d-8775-12464a0596fd_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4BvL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef196166-4e1f-449d-8775-12464a0596fd_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!4BvL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef196166-4e1f-449d-8775-12464a0596fd_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!4BvL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef196166-4e1f-449d-8775-12464a0596fd_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!4BvL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef196166-4e1f-449d-8775-12464a0596fd_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4BvL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef196166-4e1f-449d-8775-12464a0596fd_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ef196166-4e1f-449d-8775-12464a0596fd_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1628162,&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://emmanuelvalverderamos.substack.com/i/203064580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef196166-4e1f-449d-8775-12464a0596fd_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4BvL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef196166-4e1f-449d-8775-12464a0596fd_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!4BvL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef196166-4e1f-449d-8775-12464a0596fd_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!4BvL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef196166-4e1f-449d-8775-12464a0596fd_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!4BvL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef196166-4e1f-449d-8775-12464a0596fd_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#8220;Work lanes&#8221; are usually born from a good intention: protect important work from being forgotten. But when they become fixed capacity percentages, they often create the opposite effect. They hide overload, increase WIP, intensify context switching, weaken forecasts and turn product decisions into allocation disputes.</p><p>A team does not need four fictional missions. It needs one clear mission: protect and improve the user&#8217;s ability to get value from the product. That mission includes features, support, bugs, UX feedback, technical work, reliability, security, refactoring, discovery, documentation and deletion.</p><p>The demand types are real. The separate businesses are imaginary.</p><p>The question is not which lane deserves capacity. The question is which work best serves the product&#8217;s economic reality now.</p><p>A good product team should not be split into percentages like a budget spreadsheet. It should be treated as a learning and delivery system with finite attention, visible demand, limited WIP, explicit policies, enough slack to absorb reality and a shared language for economic trade-offs.</p><p>There is only one business, one product investment and one value stream. Everything else is a way of understanding what kind of demand is asking to enter.</p><p>The work was never really divided. Only the language was.</p><h2>References</h2><ul><li><p>Agile Alliance. <em>Manifesto for Agile Software Development</em>. <a href="https://agilemanifesto.org/">https://agilemanifesto.org/</a></p></li><li><p>Agile Alliance. <em>Principles behind the Agile Manifesto</em>. <a href="https://agilemanifesto.org/principles.html">https://agilemanifesto.org/principles.html</a></p></li><li><p>Artola, Luis. <em>Software Economics</em>. <a href="https://leanpub.com/software-economics">https://leanpub.com/software-economics</a></p></li><li><p>Beck, Kent. <em>Extreme Programming Explained: Embrace Change</em>. <a href="https://www.pearson.com/en-us/subject-catalog/p/extreme-programming-explained-embrace-change/P200000003514">https://www.pearson.com/en-us/subject-catalog/p/extreme-programming-explained-embrace-change/P200000003514</a></p></li><li><p>Ferro Aldama, Eduardo. <em>Menos software, m&#225;s impacto</em>. <a href="https://menos-software.eferro.net">https://menos-software.eferro.net</a></p></li><li><p>Forsgren, Nicole; Humble, Jez; Kim, Gene. <em>Accelerate: The Science of Lean Software and DevOps</em>. <a href="https://itrevolution.com/product/accelerate/">https://itrevolution.com/product/accelerate/</a></p></li><li><p>Goldratt, Eliyahu M. <em>The Goal: A Process of Ongoing Improvement</em>. <a href="https://northriverpress.com/the-goal/">https://northriverpress.com/the-goal/</a></p></li><li><p>Google Cloud DORA. <em>DORA&#8217;s Software Delivery Performance Metrics</em>. <a href="https://dora.dev/guides/dora-metrics/">https://dora.dev/guides/dora-metrics/</a></p></li><li><p>Kanban University. <em>The Official Guide to The Kanban Method</em>. <a href="https://kanban.university/kanban-guide/">https://kanban.university/kanban-guide/</a></p></li><li><p>Little, John D. C. <em>A Proof for the Queuing Formula: L = &#955;W</em>. <a href="https://pubsonline.informs.org/doi/10.1287/opre.9.3.383">https://pubsonline.informs.org/doi/10.1287/opre.9.3.383</a></p></li><li><p>Nygard, Michael T. <em>Release It! Second Edition</em>. <a href="https://pragprog.com/titles/mnee2/release-it-second-edition/">https://pragprog.com/titles/mnee2/release-it-second-edition/</a></p></li><li><p>Reinertsen, Donald G. <em>The Principles of Product Development Flow</em>. <a href="https://lpd2.com/wp-content/uploads/2013/06/ReinertsenFLOWChap1.pdf">https://lpd2.com/wp-content/uploads/2013/06/ReinertsenFLOWChap1.pdf</a></p></li><li><p>Schwaber, Ken; Sutherland, Jeff. <em>The Scrum Guide</em>. <a href="https://scrumguides.org/scrum-guide.html">https://scrumguides.org/scrum-guide.html</a></p></li><li><p>Shore, James. <em>The Art of Agile Development, 2nd Edition</em>. <a href="https://www.oreilly.com/library/view/the-art-of/9781492080688/">https://www.oreilly.com/library/view/the-art-of/9781492080688/</a></p></li><li><p>Theory of Constraints Institute. <em>Five Focusing Steps, a Process of On-Going Improvement</em>. <a href="https://www.tocinstitute.org/five-focusing-steps.html">https://www.tocinstitute.org/five-focusing-steps.html</a></p></li><li><p>Valverde Ramos, Emmanuel. <em>Output, Outcome, and Throughput: What Product Teams are really measuring when they say they deliver value</em>. <a href="https://emmanuelvalverderamos.substack.com/p/output-outcome-and-throughput-what">https://emmanuelvalverderamos.substack.com/p/output-outcome-and-throughput-what</a></p></li><li><p>Valverde Ramos, Emmanuel. <em>The Resource Utilization Trap: Why Keeping Everyone Busy Can Destroy Real Productivity</em>. <a href="https://emmanuelvalverderamos.substack.com/p/the-resource-utilization-trap-why">https://emmanuelvalverderamos.substack.com/p/the-resource-utilization-trap-why</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[How Objects Relate: A Practical Taxonomy for Object-Oriented Design]]></title><description><![CDATA[The problem is not the vocabulary. The problem is using one flat vocabulary for several different kinds of design decisions]]></description><link>https://emmanuelvalverderamos.substack.com/p/how-objects-relate-a-practical-taxonomy</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/how-objects-relate-a-practical-taxonomy</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 16 Jun 2026 05:01:01 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vPlE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Most explanations mix runtime links, type hierarchies, and design patterns into one blurry list. This article untangles them, shows where each relationship belongs, and explains the differences with real code and UML diagrams.</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_!vPlE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vPlE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!vPlE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!vPlE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!vPlE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vPlE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:859932,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.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_!vPlE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!vPlE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!vPlE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!vPlE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04c7ffa6-04a8-49cc-8c3e-26e895e0018d_1024x558.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>TL;DR</h3><p>If you learned object relationships as a flat list like this:</p><ul><li><p>Association</p></li><li><p>Dependency</p></li><li><p>Aggregation</p></li><li><p>Composition</p></li><li><p>Inheritance</p></li><li><p>Implementation</p></li></ul><p>you learned something useful, but also something incomplete.</p><p>Those terms do not all answer the same question.</p><p>A better mental model is layered:</p><ol><li><p><strong>Structural links between objects</strong><br>Association, navigability, multiplicity, qualified association, association class, self-association, aggregation, composition</p></li><li><p><strong>Type and contract relations</strong><br>Generalization, subtyping, subclassing, interface realization, classification</p></li><li><p><strong>Usage and collaboration relations</strong><br>Dependency, visibility, delegation, collaborates-with</p></li><li><p><strong>Recurring design structures</strong><br>Strategy, Decorator, Proxy, Observer, Composite</p></li></ol><p>That layered view fits the topic far better because some relationships describe <strong>who is linked</strong>, some describe <strong>what kind of thing something is</strong>, some describe <strong>who needs whom to work</strong>, and some are larger recurring arrangements built from the earlier ones.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gQ5a!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafd2571c-67b9-4999-8118-2af67cb38869_1402x1122.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gQ5a!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafd2571c-67b9-4999-8118-2af67cb38869_1402x1122.png 424w, https://substackcdn.com/image/fetch/$s_!gQ5a!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafd2571c-67b9-4999-8118-2af67cb38869_1402x1122.png 848w, https://substackcdn.com/image/fetch/$s_!gQ5a!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafd2571c-67b9-4999-8118-2af67cb38869_1402x1122.png 1272w, https://substackcdn.com/image/fetch/$s_!gQ5a!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafd2571c-67b9-4999-8118-2af67cb38869_1402x1122.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gQ5a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafd2571c-67b9-4999-8118-2af67cb38869_1402x1122.png" width="1402" height="1122" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/afd2571c-67b9-4999-8118-2af67cb38869_1402x1122.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1122,&quot;width&quot;:1402,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1508246,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafd2571c-67b9-4999-8118-2af67cb38869_1402x1122.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_!gQ5a!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafd2571c-67b9-4999-8118-2af67cb38869_1402x1122.png 424w, https://substackcdn.com/image/fetch/$s_!gQ5a!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafd2571c-67b9-4999-8118-2af67cb38869_1402x1122.png 848w, https://substackcdn.com/image/fetch/$s_!gQ5a!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafd2571c-67b9-4999-8118-2af67cb38869_1402x1122.png 1272w, https://substackcdn.com/image/fetch/$s_!gQ5a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafd2571c-67b9-4999-8118-2af67cb38869_1402x1122.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h3>Index</h3><ol><li><p>Why the usual list never quite works</p></li><li><p>The layered taxonomy</p></li><li><p>A quick UML notation decoder</p></li><li><p>Layer 1: structural links between objects</p></li><li><p>Layer 2: type and contract relations</p></li><li><p>Layer 3: usage and collaboration relations</p></li><li><p>Layer 4: recurring design structures</p></li><li><p>The confusion that wastes the most time: composition vs composition over inheritance</p></li><li><p>A practical decision guide</p></li><li><p>Conclusion</p></li><li><p>References</p></li></ol><h3>Why the usual list never quite works</h3><p>Look at these three examples:</p><ul><li><p>An <code>Order</code> contains <code>OrderLine</code> items</p></li><li><p>A <code>CheckoutService</code> delegates to a <code>PaymentGateway</code></p></li><li><p>A <code>CardPayment</code> is a <code>PaymentMethod</code></p></li></ul><p>All three are &#8220;object relationships&#8221; in a very broad sense.</p><p>But they are not the same kind of relationship.</p><p>One is about whole-part structure.<br>One is about collaboration.<br>One is about specialization and substitutability.</p><p>That is the real source of the confusion.</p><p>When these all get taught as one flat list, the words become slippery. Developers can repeat the definitions, but they still hesitate when they look at real code because they are answering different questions with the same mental drawer. The modelling literature separates structural links, type relations, and collaboration concerns much more carefully than most classroom cheat sheets do.</p><p>So the unlock for this whole topic is simple:</p><blockquote><p>Not all object relationships are relationships of the same kind.</p></blockquote><p>Once you accept that, the fog starts to lift.</p><h3>The layered taxonomy</h3><p>Here is the map we will use.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gwwi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0489d0b0-7686-46f2-9538-2ea2736ab70a_2213x396.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gwwi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0489d0b0-7686-46f2-9538-2ea2736ab70a_2213x396.png 424w, https://substackcdn.com/image/fetch/$s_!gwwi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0489d0b0-7686-46f2-9538-2ea2736ab70a_2213x396.png 848w, https://substackcdn.com/image/fetch/$s_!gwwi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0489d0b0-7686-46f2-9538-2ea2736ab70a_2213x396.png 1272w, https://substackcdn.com/image/fetch/$s_!gwwi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0489d0b0-7686-46f2-9538-2ea2736ab70a_2213x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gwwi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0489d0b0-7686-46f2-9538-2ea2736ab70a_2213x396.png" width="1456" height="261" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0489d0b0-7686-46f2-9538-2ea2736ab70a_2213x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:261,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:70990,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0489d0b0-7686-46f2-9538-2ea2736ab70a_2213x396.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_!gwwi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0489d0b0-7686-46f2-9538-2ea2736ab70a_2213x396.png 424w, https://substackcdn.com/image/fetch/$s_!gwwi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0489d0b0-7686-46f2-9538-2ea2736ab70a_2213x396.png 848w, https://substackcdn.com/image/fetch/$s_!gwwi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0489d0b0-7686-46f2-9538-2ea2736ab70a_2213x396.png 1272w, https://substackcdn.com/image/fetch/$s_!gwwi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0489d0b0-7686-46f2-9538-2ea2736ab70a_2213x396.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Each layer answers a different question:</p><ul><li><p><strong>Structural links</strong> ask: who is related to whom?</p></li><li><p><strong>Type and contract relations</strong> ask: what kind of thing is this, and what can stand in for what?</p></li><li><p><strong>Usage and collaboration relations</strong> ask: who needs whom to get work done?</p></li><li><p><strong>Recurring design structures</strong> ask: what larger arrangements show up again and again in good designs?</p></li></ul><p>This is the most useful mental model in the whole article. It turns a memorization problem into a judgment problem.</p><h3>A quick UML notation decoder</h3><p>Before going deeper, it helps to know what the main arrows and markers are trying to say.</p><ul><li><p><strong>Association</strong>: solid line. A stable structural relation.</p></li><li><p><strong>Dependency</strong>: dashed arrow. Temporary use or reliance.</p></li><li><p><strong>Generalization / inheritance</strong>: solid line with hollow triangle toward the more general type.</p></li><li><p><strong>Interface realization</strong>: dashed line with hollow triangle toward the interface.</p></li><li><p><strong>Aggregation</strong>: hollow diamond at the whole side.</p></li><li><p><strong>Composition</strong>: filled diamond at the whole side.</p></li><li><p><strong>Multiplicity</strong>: labels like <code>1</code>, <code>0..1</code>, <code>*</code>, <code>1..*</code>.</p></li><li><p><strong>Qualified association</strong>: a key such as <code>[sku]</code> is shown at the association end.</p></li><li><p><strong>Association class</strong>: the relationship itself is promoted to a class with its own data.</p></li></ul><p>A compact picture helps:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FfwV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0305bf-1996-4bfc-aa50-17b783ddbfd5_1956x516.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FfwV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0305bf-1996-4bfc-aa50-17b783ddbfd5_1956x516.png 424w, https://substackcdn.com/image/fetch/$s_!FfwV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0305bf-1996-4bfc-aa50-17b783ddbfd5_1956x516.png 848w, https://substackcdn.com/image/fetch/$s_!FfwV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0305bf-1996-4bfc-aa50-17b783ddbfd5_1956x516.png 1272w, https://substackcdn.com/image/fetch/$s_!FfwV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0305bf-1996-4bfc-aa50-17b783ddbfd5_1956x516.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FfwV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0305bf-1996-4bfc-aa50-17b783ddbfd5_1956x516.png" width="1456" height="384" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1d0305bf-1996-4bfc-aa50-17b783ddbfd5_1956x516.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:384,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:76774,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0305bf-1996-4bfc-aa50-17b783ddbfd5_1956x516.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_!FfwV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0305bf-1996-4bfc-aa50-17b783ddbfd5_1956x516.png 424w, https://substackcdn.com/image/fetch/$s_!FfwV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0305bf-1996-4bfc-aa50-17b783ddbfd5_1956x516.png 848w, https://substackcdn.com/image/fetch/$s_!FfwV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0305bf-1996-4bfc-aa50-17b783ddbfd5_1956x516.png 1272w, https://substackcdn.com/image/fetch/$s_!FfwV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0305bf-1996-4bfc-aa50-17b783ddbfd5_1956x516.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>UML is not trying to decorate boxes with different arrows. It is trying to encode different kinds of meaning.</p><h2>1. Layer 1: Structural links between objects</h2><p>This is the most intuitive layer.</p><p>It answers the most natural question:</p><p><strong>Who is linked to whom?</strong></p><h3>1.1 Association</h3><p>An <strong>association</strong> is a stable semantic link between objects.</p><p>In plain English:</p><p>One object knows about, refers to, or is related to another in a way that is part of the object model.</p><p>A simple example:</p><ul><li><p>an <code>Order</code> is placed by a <code>Customer</code></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WgGE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464a4182-be92-4c27-ae1f-b44815ed1b47_222x516.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WgGE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464a4182-be92-4c27-ae1f-b44815ed1b47_222x516.png 424w, https://substackcdn.com/image/fetch/$s_!WgGE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464a4182-be92-4c27-ae1f-b44815ed1b47_222x516.png 848w, https://substackcdn.com/image/fetch/$s_!WgGE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464a4182-be92-4c27-ae1f-b44815ed1b47_222x516.png 1272w, https://substackcdn.com/image/fetch/$s_!WgGE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464a4182-be92-4c27-ae1f-b44815ed1b47_222x516.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WgGE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464a4182-be92-4c27-ae1f-b44815ed1b47_222x516.png" width="144" height="334.7027027027027" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/464a4182-be92-4c27-ae1f-b44815ed1b47_222x516.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:516,&quot;width&quot;:222,&quot;resizeWidth&quot;:144,&quot;bytes&quot;:13221,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464a4182-be92-4c27-ae1f-b44815ed1b47_222x516.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_!WgGE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464a4182-be92-4c27-ae1f-b44815ed1b47_222x516.png 424w, https://substackcdn.com/image/fetch/$s_!WgGE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464a4182-be92-4c27-ae1f-b44815ed1b47_222x516.png 848w, https://substackcdn.com/image/fetch/$s_!WgGE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464a4182-be92-4c27-ae1f-b44815ed1b47_222x516.png 1272w, https://substackcdn.com/image/fetch/$s_!WgGE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464a4182-be92-4c27-ae1f-b44815ed1b47_222x516.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;6f41fd44-948e-408e-b69e-f766c04f32da&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class CustomerId(val value: String)
data class OrderId(val value: String)

class Customer(
    val id: CustomerId,
    val fullName: String
)

class Order(
    val id: OrderId,
    val placedBy: Customer
)</code></pre></div><p>Why this is association:</p><ul><li><p>The link is part of the structure of <code>Order</code></p></li><li><p>The relationship is stable enough to live in the model</p></li><li><p>You can ask an order who placed it</p></li></ul><p>The important point is not &#8220;there is contact between two objects.&#8221; The important point is that the relationship belongs to the structure of the model. That is what makes this different from a temporary dependency.</p><h3>1.2 Navigability and multiplicity</h3><p>Once you have an association, two more questions matter:</p><ul><li><p>Can one side navigate to the other?</p></li><li><p>How many instances can participate?</p></li></ul><p>These sound small, but they change the meaning of the model.</p><h4>Unidirectional association</h4><p><code>Order</code> knows its <code>Customer</code>, but <code>Customer</code> does not expose orders directly.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;278aa139-441c-44cf-a813-cf4a36f116ab&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class CustomerId(val value: String)
data class OrderId(val value: String)

class Order(
    val id: OrderId,
    val placedBy: Customer
)</code></pre></div><h4>Bidirectional association</h4><p>Now <code>Customer</code> also tracks its orders.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;1035cc7a-887f-4dc8-826b-d87075402fcf&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class CustomerId(val value: String)

class Customer(
    val id: CustomerId,
    val fullName: String
) {
    private val orders = mutableListOf&lt;Order&gt;()

    fun addOrder(order: Order) {
        orders += order
    }

    fun orders(): List&lt;Order&gt; = orders.toList()
}</code></pre></div><p>This is not about ownership.</p><p>It is about <strong>traversability</strong>.</p><p>And multiplicity matters too:</p><ul><li><p>One customer may place many orders</p></li><li><p>Each order usually has one customer</p></li></ul><p>Association tells you that a link exists. Navigability tells you who can follow it. Multiplicity tells you how many related instances there may be. Keeping those separate makes diagrams and code much easier to read.</p><blockquote><p>Association tells you that a link exists. Navigability tells you who can follow it. Multiplicity tells you how many links there may be.</p></blockquote><h3>1.3 Self-association</h3><p>A class can be associated with itself.</p><p>That sounds advanced, but it is ordinary.</p><p>A simple example:</p><ul><li><p>An employee mentors another employee</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DOGX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55e0cade-da08-4dd8-9f0c-9e332a68e22e_236x448.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DOGX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55e0cade-da08-4dd8-9f0c-9e332a68e22e_236x448.png 424w, https://substackcdn.com/image/fetch/$s_!DOGX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55e0cade-da08-4dd8-9f0c-9e332a68e22e_236x448.png 848w, https://substackcdn.com/image/fetch/$s_!DOGX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55e0cade-da08-4dd8-9f0c-9e332a68e22e_236x448.png 1272w, https://substackcdn.com/image/fetch/$s_!DOGX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55e0cade-da08-4dd8-9f0c-9e332a68e22e_236x448.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DOGX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55e0cade-da08-4dd8-9f0c-9e332a68e22e_236x448.png" width="144" height="273.35593220338984" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/55e0cade-da08-4dd8-9f0c-9e332a68e22e_236x448.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:448,&quot;width&quot;:236,&quot;resizeWidth&quot;:144,&quot;bytes&quot;:13371,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55e0cade-da08-4dd8-9f0c-9e332a68e22e_236x448.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_!DOGX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55e0cade-da08-4dd8-9f0c-9e332a68e22e_236x448.png 424w, https://substackcdn.com/image/fetch/$s_!DOGX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55e0cade-da08-4dd8-9f0c-9e332a68e22e_236x448.png 848w, https://substackcdn.com/image/fetch/$s_!DOGX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55e0cade-da08-4dd8-9f0c-9e332a68e22e_236x448.png 1272w, https://substackcdn.com/image/fetch/$s_!DOGX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55e0cade-da08-4dd8-9f0c-9e332a68e22e_236x448.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;b693f162-8fb2-44d3-a301-f967cae106b9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class EmployeeId(val value: String)

class Employee(
    val id: EmployeeId,
    val fullName: String
) {
    private val mentees = mutableListOf&lt;Employee&gt;()

    fun mentor(mentee: Employee) {
        mentees += mentee
    }

    fun mentees(): List&lt;Employee&gt; = mentees.toList()
}</code></pre></div><p>This is still association.</p><p>It is not inheritance.</p><p>That matters because the same class appears on both ends, but the question is still &#8220;who relates to whom?&#8221;, not &#8220;what kind of thing is this?&#8221;.</p><h3>1.4 Qualified association</h3><p>A <strong>qualified association</strong> is a relationship accessed through a key.</p><p>In software terms, the easiest way to picture it is this:</p><p>one object has access to a larger related set, but reaches a specific related object by using a qualifier such as an identifier.</p><p>A clean example is a product catalog looking up product descriptions by SKU.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eAzA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F278e3f5c-2a4c-494b-b1b6-6a95751a81c6_367x516.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eAzA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F278e3f5c-2a4c-494b-b1b6-6a95751a81c6_367x516.png 424w, https://substackcdn.com/image/fetch/$s_!eAzA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F278e3f5c-2a4c-494b-b1b6-6a95751a81c6_367x516.png 848w, https://substackcdn.com/image/fetch/$s_!eAzA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F278e3f5c-2a4c-494b-b1b6-6a95751a81c6_367x516.png 1272w, https://substackcdn.com/image/fetch/$s_!eAzA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F278e3f5c-2a4c-494b-b1b6-6a95751a81c6_367x516.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eAzA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F278e3f5c-2a4c-494b-b1b6-6a95751a81c6_367x516.png" width="151" height="212.30517711171663" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/278e3f5c-2a4c-494b-b1b6-6a95751a81c6_367x516.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:516,&quot;width&quot;:367,&quot;resizeWidth&quot;:151,&quot;bytes&quot;:17895,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F278e3f5c-2a4c-494b-b1b6-6a95751a81c6_367x516.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_!eAzA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F278e3f5c-2a4c-494b-b1b6-6a95751a81c6_367x516.png 424w, https://substackcdn.com/image/fetch/$s_!eAzA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F278e3f5c-2a4c-494b-b1b6-6a95751a81c6_367x516.png 848w, https://substackcdn.com/image/fetch/$s_!eAzA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F278e3f5c-2a4c-494b-b1b6-6a95751a81c6_367x516.png 1272w, https://substackcdn.com/image/fetch/$s_!eAzA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F278e3f5c-2a4c-494b-b1b6-6a95751a81c6_367x516.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;259cb563-c4fb-4452-a9aa-51501dd0cace&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class ProductDescription(
    val sku: String,
    val name: String
)

class ProductCatalog(
    products: List&lt;ProductDescription&gt;
) {
    private val productsBySku = products.associateBy { it.sku }

    fun findBySku(sku: String): ProductDescription =
        productsBySku[sku] ?: error("Unknown SKU: $sku")
}</code></pre></div><p>Why this matters:</p><ul><li><p>It is not just &#8220;the catalog has many products&#8221;</p></li><li><p>The key is part of how the relationship is understood</p></li><li><p>It turns lookup into part of the model rather than leaving it as a hidden implementation detail</p></li></ul><p>A useful side effect of qualification is that it often reduces multiplicity at the target end, because the key usually selects one object from a larger set.</p><h3>1.5 Association class</h3><p>Sometimes the relationship itself has meaningful data.</p><p>That is the moment a plain association stops being enough.</p><p>A classic example:</p><ul><li><p>A student is enrolled in a course</p></li><li><p>But the <strong>enrollment</strong> has its own data: date, status, grade</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!riIL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20ac863b-5ba2-48d0-ba75-a3ff6aa626cf_446x588.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!riIL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20ac863b-5ba2-48d0-ba75-a3ff6aa626cf_446x588.png 424w, https://substackcdn.com/image/fetch/$s_!riIL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20ac863b-5ba2-48d0-ba75-a3ff6aa626cf_446x588.png 848w, https://substackcdn.com/image/fetch/$s_!riIL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20ac863b-5ba2-48d0-ba75-a3ff6aa626cf_446x588.png 1272w, https://substackcdn.com/image/fetch/$s_!riIL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20ac863b-5ba2-48d0-ba75-a3ff6aa626cf_446x588.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!riIL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20ac863b-5ba2-48d0-ba75-a3ff6aa626cf_446x588.png" width="204" height="268.9506726457399" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/20ac863b-5ba2-48d0-ba75-a3ff6aa626cf_446x588.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:588,&quot;width&quot;:446,&quot;resizeWidth&quot;:204,&quot;bytes&quot;:24124,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20ac863b-5ba2-48d0-ba75-a3ff6aa626cf_446x588.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_!riIL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20ac863b-5ba2-48d0-ba75-a3ff6aa626cf_446x588.png 424w, https://substackcdn.com/image/fetch/$s_!riIL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20ac863b-5ba2-48d0-ba75-a3ff6aa626cf_446x588.png 848w, https://substackcdn.com/image/fetch/$s_!riIL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20ac863b-5ba2-48d0-ba75-a3ff6aa626cf_446x588.png 1272w, https://substackcdn.com/image/fetch/$s_!riIL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20ac863b-5ba2-48d0-ba75-a3ff6aa626cf_446x588.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;7c5d0a59-b86c-40ea-9999-f2619c4d0f99&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class StudentId(val value: String)
data class CourseId(val value: String)

class Student(
    val id: StudentId,
    val fullName: String
)

class Course(
    val id: CourseId,
    val title: String
)

class Enrollment(
    val student: Student,
    val course: Course,
    val enrolledOn: String,
    val status: String
)</code></pre></div><p>Why this is not just a many-to-many association:</p><ul><li><p>The relationship now has its own information</p></li><li><p>The relationship becomes a concept worth naming</p></li><li><p>The model becomes clearer once the link is promoted to a first-class citizen</p></li></ul><p>Many messy many-to-many models are really unnamed association classes. That is one of the most useful modelling upgrades developers learn once they stop treating associations as only lines between boxes.</p><blockquote><p>Many messy many-to-many designs are really unnamed association classes.</p></blockquote><h3>1.6 Aggregation</h3><p>I want to be very explicit here.</p><p>I am keeping aggregation <strong>brief and precise on purpose</strong>.</p><p>Not because it does not exist.<br>Not because you will never see it.<br>But because it is one of the least useful distinctions in the standard list.</p><p>A practical definition is this:</p><p><strong>aggregation is a weak whole-part relation where the part can exist independently of the whole</strong></p><p>A small example:</p><ul><li><p>A <code>Department</code> has <code>Professor</code> objects</p></li><li><p>A professor can exist without that department</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-WGv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3fd0736-a483-4d59-9e9a-c1ddfccab944_258x516.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-WGv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3fd0736-a483-4d59-9e9a-c1ddfccab944_258x516.png 424w, https://substackcdn.com/image/fetch/$s_!-WGv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3fd0736-a483-4d59-9e9a-c1ddfccab944_258x516.png 848w, https://substackcdn.com/image/fetch/$s_!-WGv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3fd0736-a483-4d59-9e9a-c1ddfccab944_258x516.png 1272w, https://substackcdn.com/image/fetch/$s_!-WGv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3fd0736-a483-4d59-9e9a-c1ddfccab944_258x516.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-WGv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3fd0736-a483-4d59-9e9a-c1ddfccab944_258x516.png" width="184" height="368" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d3fd0736-a483-4d59-9e9a-c1ddfccab944_258x516.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:516,&quot;width&quot;:258,&quot;resizeWidth&quot;:184,&quot;bytes&quot;:13485,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3fd0736-a483-4d59-9e9a-c1ddfccab944_258x516.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_!-WGv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3fd0736-a483-4d59-9e9a-c1ddfccab944_258x516.png 424w, https://substackcdn.com/image/fetch/$s_!-WGv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3fd0736-a483-4d59-9e9a-c1ddfccab944_258x516.png 848w, https://substackcdn.com/image/fetch/$s_!-WGv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3fd0736-a483-4d59-9e9a-c1ddfccab944_258x516.png 1272w, https://substackcdn.com/image/fetch/$s_!-WGv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3fd0736-a483-4d59-9e9a-c1ddfccab944_258x516.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;7d26fc0d-6cf7-4170-85bb-4cac783ddabb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">data class ProfessorId(val value: String)

class Professor(
    val id: ProfessorId,
    val fullName: String
)

class Department(
    val name: String
) {
    private val professors = mutableListOf&lt;Professor&gt;()

    fun addProfessor(professor: Professor) {
        professors += professor
    }

    fun professors(): List&lt;Professor&gt; = professors.toList()
}</code></pre></div><p>Why I am not giving aggregation more space:</p><ul><li><p>It is intentionally weak</p></li><li><p>It often says little that plain association does not already say</p></li><li><p>It is much less actionable than composition in real modelling work</p></li></ul><p>That is why aggregation deserves recognition, but not obsession. If association already says enough, use association. If lifecycle ownership matters, use composition.</p><blockquote><p>If association already says enough, use association. If lifecycle ownership matters, use composition.</p></blockquote><h3>1.7 Composition</h3><p><strong>Composition</strong> is the strong whole-part relation.</p><p>This one matters much more.</p><p>The practical test is simple:</p><ul><li><p>The part belongs to one whole at a time</p></li><li><p>The part is not conceptually free-floating</p></li><li><p>The whole is responsible for the part&#8217;s lifecycle</p></li></ul><p>A very good example is <code>Order</code> and <code>OrderLine</code>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!L6TO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed8321ff-5954-418d-a995-9acbe85d881f_231x516.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!L6TO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed8321ff-5954-418d-a995-9acbe85d881f_231x516.png 424w, https://substackcdn.com/image/fetch/$s_!L6TO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed8321ff-5954-418d-a995-9acbe85d881f_231x516.png 848w, https://substackcdn.com/image/fetch/$s_!L6TO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed8321ff-5954-418d-a995-9acbe85d881f_231x516.png 1272w, https://substackcdn.com/image/fetch/$s_!L6TO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed8321ff-5954-418d-a995-9acbe85d881f_231x516.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!L6TO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed8321ff-5954-418d-a995-9acbe85d881f_231x516.png" width="199" height="444.5194805194805" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ed8321ff-5954-418d-a995-9acbe85d881f_231x516.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:516,&quot;width&quot;:231,&quot;resizeWidth&quot;:199,&quot;bytes&quot;:12609,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed8321ff-5954-418d-a995-9acbe85d881f_231x516.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_!L6TO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed8321ff-5954-418d-a995-9acbe85d881f_231x516.png 424w, https://substackcdn.com/image/fetch/$s_!L6TO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed8321ff-5954-418d-a995-9acbe85d881f_231x516.png 848w, https://substackcdn.com/image/fetch/$s_!L6TO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed8321ff-5954-418d-a995-9acbe85d881f_231x516.png 1272w, https://substackcdn.com/image/fetch/$s_!L6TO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed8321ff-5954-418d-a995-9acbe85d881f_231x516.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;925abf21-38c4-403e-9b55-836ef08c8e5b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class OrderLine(
    val product: ProductDescription,
    val quantity: Int
)

class Order(
    val id: OrderId
) {
    private val lines = mutableListOf&lt;OrderLine&gt;()

    fun addLine(product: ProductDescription, quantity: Int) {
        lines += OrderLine(product, quantity)
    }

    fun lines(): List&lt;OrderLine&gt; = lines.toList()
}</code></pre></div><p>Why this is composition:</p><ul><li><p>An order line exists as part of an order</p></li><li><p>The order controls creation of its lines</p></li><li><p>Lifecycle ownership is the point of the relationship</p></li></ul><p>Association says linked. Composition says owned as part of the whole. That is the real distinction worth remembering.</p><blockquote><p>Association says linked. Composition says owned as part of the whole.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kMwr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301ce12b-ae58-4415-82bf-015669c62e8d_802x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kMwr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301ce12b-ae58-4415-82bf-015669c62e8d_802x559.png 424w, https://substackcdn.com/image/fetch/$s_!kMwr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301ce12b-ae58-4415-82bf-015669c62e8d_802x559.png 848w, https://substackcdn.com/image/fetch/$s_!kMwr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301ce12b-ae58-4415-82bf-015669c62e8d_802x559.png 1272w, https://substackcdn.com/image/fetch/$s_!kMwr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301ce12b-ae58-4415-82bf-015669c62e8d_802x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kMwr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301ce12b-ae58-4415-82bf-015669c62e8d_802x559.png" width="802" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/301ce12b-ae58-4415-82bf-015669c62e8d_802x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:802,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:446696,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4abc5bb-f827-49e2-b999-a205fa163aec_1024x559.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_!kMwr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301ce12b-ae58-4415-82bf-015669c62e8d_802x559.png 424w, https://substackcdn.com/image/fetch/$s_!kMwr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301ce12b-ae58-4415-82bf-015669c62e8d_802x559.png 848w, https://substackcdn.com/image/fetch/$s_!kMwr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301ce12b-ae58-4415-82bf-015669c62e8d_802x559.png 1272w, https://substackcdn.com/image/fetch/$s_!kMwr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301ce12b-ae58-4415-82bf-015669c62e8d_802x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>2. Layer 2: Type and contract relations</h2><p>Now the question changes.</p><p>We are no longer asking:</p><p><strong>Who is linked to whom?</strong></p><p>We are asking:</p><ul><li><p>What kind of thing is this?</p></li><li><p>What contract does it satisfy?</p></li><li><p>What may stand in for what?</p></li></ul><p>That is where inheritance belongs.</p><h3>2.1 Generalization and inheritance</h3><p>A <strong>generalization</strong> says that one thing is a more specific kind of another.</p><p>A simple example:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!u1-v!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c8256c9-f278-4c3e-ab65-cc38a409d317_749x468.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u1-v!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c8256c9-f278-4c3e-ab65-cc38a409d317_749x468.png 424w, https://substackcdn.com/image/fetch/$s_!u1-v!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c8256c9-f278-4c3e-ab65-cc38a409d317_749x468.png 848w, https://substackcdn.com/image/fetch/$s_!u1-v!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c8256c9-f278-4c3e-ab65-cc38a409d317_749x468.png 1272w, https://substackcdn.com/image/fetch/$s_!u1-v!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c8256c9-f278-4c3e-ab65-cc38a409d317_749x468.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!u1-v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c8256c9-f278-4c3e-ab65-cc38a409d317_749x468.png" width="655" height="409.2656875834446" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9c8256c9-f278-4c3e-ab65-cc38a409d317_749x468.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:468,&quot;width&quot;:749,&quot;resizeWidth&quot;:655,&quot;bytes&quot;:28092,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c8256c9-f278-4c3e-ab65-cc38a409d317_749x468.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_!u1-v!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c8256c9-f278-4c3e-ab65-cc38a409d317_749x468.png 424w, https://substackcdn.com/image/fetch/$s_!u1-v!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c8256c9-f278-4c3e-ab65-cc38a409d317_749x468.png 848w, https://substackcdn.com/image/fetch/$s_!u1-v!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c8256c9-f278-4c3e-ab65-cc38a409d317_749x468.png 1272w, https://substackcdn.com/image/fetch/$s_!u1-v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c8256c9-f278-4c3e-ab65-cc38a409d317_749x468.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;a86ccf5e-d08b-42f1-ace0-a23b02395d76&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">sealed class PaymentMethod {
    abstract fun authorize(amountInCents: Int): PaymentAuthorization
}

class CardPayment(
    private val gateway: PaymentGateway
) : PaymentMethod() {
    override fun authorize(amountInCents: Int): PaymentAuthorization =
        gateway.authorize(amountInCents)
}

class BankTransferPayment : PaymentMethod() {
    override fun authorize(amountInCents: Int): PaymentAuthorization =
        PaymentAuthorization.approved()
}

class PaymentAuthorization private constructor(
    val approved: Boolean
) {
    companion object {
        fun approved(): PaymentAuthorization = PaymentAuthorization(true)
    }
}</code></pre></div><p>Why this is different from association:</p><ul><li><p>The question is not &#8220;<strong>what is linked?</strong>&#8221;</p></li><li><p>The question is &#8220;<strong>what kind of thing is this?</strong>&#8221;</p></li><li><p><code>CardPayment</code> <strong>is a</strong> <code>PaymentMethod</code></p></li></ul><p>This layer is about taxonomy, substitutability, and contract, not about runtime links between objects.</p><h3>2.2 Subtyping vs subclassing</h3><p>This is one of the most important distinctions in the whole article.</p><p>They are related, but not identical.</p><ul><li><p><strong>subtyping</strong> is about contract and substitutability</p></li><li><p><strong>subclassing</strong> is about inheriting implementation structure</p></li></ul><p>You can have subtyping without shared implementation inheritance.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;4ad37cef-9a62-44b1-81aa-dcea6df769cc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface ReceiptFormatter {
    fun format(order: Order): String
}

class PlainTextReceiptFormatter : ReceiptFormatter {
    override fun format(order: Order): String =
        "Order ${order.id.value}"
}

class HtmlReceiptFormatter : ReceiptFormatter {
    override fun format(order: Order): String =
        "&lt;strong&gt;Order ${order.id.value}&lt;/strong&gt;"
}</code></pre></div><p>Both classes satisfy the same contract.</p><p>Neither inherits implementation from a base class.</p><p>That difference matters because many bad hierarchies come from confusing reuse with meaning.</p><p>A subtype promises. A subclass reuses. Sometimes one does both. They are still not the same idea. And when you confuse them, you start inheriting because code is nearby instead of inheriting because meaning and substitutability are truly there.</p><blockquote><p>A subtype promises. A subclass reuses. Sometimes one does both. They are still not the same idea.</p></blockquote><h3>2.3 Interface realization</h3><p>When a class satisfies an interface contract, UML calls that <strong>interface realization</strong>.</p><p>That is a better phrase than the loose classroom word &#8220;implementation&#8221; because it tells you what matters most: the class is realizing a contract.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ocfH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52088802-8047-4e6b-93fe-76fe3f2e6473_427x516.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ocfH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52088802-8047-4e6b-93fe-76fe3f2e6473_427x516.png 424w, https://substackcdn.com/image/fetch/$s_!ocfH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52088802-8047-4e6b-93fe-76fe3f2e6473_427x516.png 848w, https://substackcdn.com/image/fetch/$s_!ocfH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52088802-8047-4e6b-93fe-76fe3f2e6473_427x516.png 1272w, https://substackcdn.com/image/fetch/$s_!ocfH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52088802-8047-4e6b-93fe-76fe3f2e6473_427x516.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ocfH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52088802-8047-4e6b-93fe-76fe3f2e6473_427x516.png" width="351" height="424.1592505854801" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/52088802-8047-4e6b-93fe-76fe3f2e6473_427x516.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:516,&quot;width&quot;:427,&quot;resizeWidth&quot;:351,&quot;bytes&quot;:22927,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52088802-8047-4e6b-93fe-76fe3f2e6473_427x516.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_!ocfH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52088802-8047-4e6b-93fe-76fe3f2e6473_427x516.png 424w, https://substackcdn.com/image/fetch/$s_!ocfH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52088802-8047-4e6b-93fe-76fe3f2e6473_427x516.png 848w, https://substackcdn.com/image/fetch/$s_!ocfH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52088802-8047-4e6b-93fe-76fe3f2e6473_427x516.png 1272w, https://substackcdn.com/image/fetch/$s_!ocfH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52088802-8047-4e6b-93fe-76fe3f2e6473_427x516.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;2e324a1c-fb42-4e44-883d-73e990affd72&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface PaymentGateway {
    fun authorize(amountInCents: Int): PaymentAuthorization
}

class StripePaymentGateway : PaymentGateway {
    override fun authorize(amountInCents: Int): PaymentAuthorization =
        PaymentAuthorization.approved()
}</code></pre></div><p>Why this is different from inheritance:</p><ul><li><p>No shared base implementation is required</p></li><li><p>The focus is contract conformance</p></li><li><p>It belongs to the type-and-contract layer, not the structural link layer</p></li></ul><p>This becomes especially useful when you build systems around delegation, substitution, and dependency inversion instead of rigid hierarchies.</p><h3>2.4 Classification is richer than one inheritance tree</h3><p>One of the easiest traps in object modelling is treating every domain distinction as a subclassing problem.</p><p>But classification is richer than that.</p><p>A customer might be:</p><ul><li><p>Corporate</p></li><li><p>Priority</p></li><li><p>Suspended</p></li><li><p>Credit-approved</p></li></ul><p>If you try to force all of those into one inheritance tree, you quickly create either nonsense or class explosion.</p><p>Real domains often contain several independent classification dimensions, and inheritance trees are frequently a poor fit for that. This becomes even clearer when you consider multiple classification and dynamic classification, both of which show that type can be more fluid and more multi-dimensional than mainstream inheritance trees suggest.</p><p>That matters because it teaches a deeper lesson:</p><p>sometimes the right answer is not &#8220;build a bigger hierarchy.&#8221;</p><p>Sometimes the right answer is &#8220;this distinction belongs to state, role, policy, or collaboration instead.&#8221;</p><h2>3. Layer 3: Usage and collaboration relations</h2><p>Now the question changes again.</p><p>We are no longer asking who is linked or what kind of thing something is.</p><p>We are asking:</p><ul><li><p>Who needs whom to do work?</p></li><li><p>How does one object gain access to another?</p></li><li><p>When is the relationship structural, and when is it just operational?</p></li></ul><h3>3.1 Dependency</h3><p>A <strong>dependency</strong> is a weaker relation than association.</p><p>A simple rule of thumb:</p><p>if one object only needs another object temporarily in order to do some work, you are usually looking at a dependency rather than a structural association.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!21B6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a94eec9-af6e-4563-a8e1-6821d526f9b8_257x516.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!21B6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a94eec9-af6e-4563-a8e1-6821d526f9b8_257x516.png 424w, https://substackcdn.com/image/fetch/$s_!21B6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a94eec9-af6e-4563-a8e1-6821d526f9b8_257x516.png 848w, https://substackcdn.com/image/fetch/$s_!21B6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a94eec9-af6e-4563-a8e1-6821d526f9b8_257x516.png 1272w, https://substackcdn.com/image/fetch/$s_!21B6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a94eec9-af6e-4563-a8e1-6821d526f9b8_257x516.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!21B6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a94eec9-af6e-4563-a8e1-6821d526f9b8_257x516.png" width="195" height="391.5175097276265" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8a94eec9-af6e-4563-a8e1-6821d526f9b8_257x516.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:516,&quot;width&quot;:257,&quot;resizeWidth&quot;:195,&quot;bytes&quot;:13553,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a94eec9-af6e-4563-a8e1-6821d526f9b8_257x516.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_!21B6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a94eec9-af6e-4563-a8e1-6821d526f9b8_257x516.png 424w, https://substackcdn.com/image/fetch/$s_!21B6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a94eec9-af6e-4563-a8e1-6821d526f9b8_257x516.png 848w, https://substackcdn.com/image/fetch/$s_!21B6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a94eec9-af6e-4563-a8e1-6821d526f9b8_257x516.png 1272w, https://substackcdn.com/image/fetch/$s_!21B6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a94eec9-af6e-4563-a8e1-6821d526f9b8_257x516.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;61907c16-0b38-49b0-aba3-2bdf044022f1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface TaxPolicy {
    fun calculateTax(netAmountInCents: Int): Int
}

class OrderPricer {
    fun calculateTotal(netAmountInCents: Int, taxPolicy: TaxPolicy): Int {
        val tax = taxPolicy.calculateTax(netAmountInCents)
        return netAmountInCents + tax
    }
}</code></pre></div><p>Why this is dependency:</p><ul><li><p>The relation is operational</p></li><li><p>The pricer does not need to keep the policy as part of its identity</p></li><li><p>The other object is needed to perform an action, not define structure</p></li></ul><p>Association tends to live in the object. Dependency tends to live in the action.</p><blockquote><p>Association tends to live in the object. Dependency tends to live in the action.</p></blockquote><h3>3.2 Visibility: how collaboration becomes possible</h3><p>In code, collaboration does not happen by magic.</p><p>One object needs some path to another.</p><p>That is where <strong>visibility</strong> matters.</p><p>A practical set of forms is:</p><ul><li><p>Attribute visibility</p></li><li><p>Parameter visibility</p></li><li><p>Local visibility</p></li><li><p>Global visibility</p></li></ul><h4>Attribute visibility</h4><p>The object keeps the collaborator as state.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;fc1c746b-e252-4a32-84d1-af52b317077a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class CheckoutService(
    private val paymentGateway: PaymentGateway
) {
    fun checkout(amountInCents: Int): PaymentAuthorization =
        paymentGateway.authorize(amountInCents)
}</code></pre></div><h4>Parameter visibility</h4><p>The collaborator is passed in only for the current call.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;b7177a47-9ef4-4ab7-8449-517349d7510a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class OrderPricer {
    fun calculateTotal(netAmountInCents: Int, taxPolicy: TaxPolicy): Int =
        netAmountInCents + taxPolicy.calculateTax(netAmountInCents)
}</code></pre></div><h4>Local visibility</h4><p>The collaborator is created or captured inside the method.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;f85490e6-850b-495c-93bc-033f77d7aa52&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class ReceiptService {
    fun buildReceipt(order: Order): String {
        val formatter = PlainTextReceiptFormatter()
        return formatter.format(order)
    }
}</code></pre></div><h4>Global visibility</h4><p>The collaborator is globally reachable.</p><p>This exists, but it is usually the least interesting and often the least desirable form in object design.</p><p>Why this matters:</p><p>visibility is not exactly a domain relationship.</p><p>It is the access path that makes collaboration possible in the design.</p><p>That is subtle, but very practical.</p><h3>3.3 Delegation</h3><p>Delegation is one of the most important object relationships in day-to-day design.</p><p>One object receives a request, then hands responsibility to another object that is better suited to do the work.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7f9w!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a32c10e-18c0-44f2-a5aa-e6a7714f3f7f_1073x534.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7f9w!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a32c10e-18c0-44f2-a5aa-e6a7714f3f7f_1073x534.png 424w, https://substackcdn.com/image/fetch/$s_!7f9w!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a32c10e-18c0-44f2-a5aa-e6a7714f3f7f_1073x534.png 848w, https://substackcdn.com/image/fetch/$s_!7f9w!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a32c10e-18c0-44f2-a5aa-e6a7714f3f7f_1073x534.png 1272w, https://substackcdn.com/image/fetch/$s_!7f9w!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a32c10e-18c0-44f2-a5aa-e6a7714f3f7f_1073x534.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7f9w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a32c10e-18c0-44f2-a5aa-e6a7714f3f7f_1073x534.png" width="1073" height="534" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6a32c10e-18c0-44f2-a5aa-e6a7714f3f7f_1073x534.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:534,&quot;width&quot;:1073,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:48021,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a32c10e-18c0-44f2-a5aa-e6a7714f3f7f_1073x534.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_!7f9w!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a32c10e-18c0-44f2-a5aa-e6a7714f3f7f_1073x534.png 424w, https://substackcdn.com/image/fetch/$s_!7f9w!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a32c10e-18c0-44f2-a5aa-e6a7714f3f7f_1073x534.png 848w, https://substackcdn.com/image/fetch/$s_!7f9w!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a32c10e-18c0-44f2-a5aa-e6a7714f3f7f_1073x534.png 1272w, https://substackcdn.com/image/fetch/$s_!7f9w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a32c10e-18c0-44f2-a5aa-e6a7714f3f7f_1073x534.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;fed3306d-c94e-41fa-bd91-757d85aa359d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">class CheckoutService(
    private val paymentGateway: PaymentGateway
) {
    fun checkout(amountInCents: Int): PaymentAuthorization =
        paymentGateway.authorize(amountInCents)
}</code></pre></div><p>Why this matters:</p><ul><li><p>Delegation is not inheritance</p></li><li><p>Delegation is not UML composition</p></li><li><p>Delegation is about behavioral handoff</p></li></ul><p>It is often the quiet answer to problems that people first try to solve with inheritance. If a class really just needs another object to perform a behavior, delegation is usually clearer than pretending one object is a more specific kind of the other.</p><blockquote><p>Delegation is often the quiet answer to problems that people first try to solve with inheritance.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zvFo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a7e36d-2714-4b9b-9058-d47fc158c799_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zvFo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a7e36d-2714-4b9b-9058-d47fc158c799_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!zvFo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a7e36d-2714-4b9b-9058-d47fc158c799_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!zvFo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a7e36d-2714-4b9b-9058-d47fc158c799_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!zvFo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a7e36d-2714-4b9b-9058-d47fc158c799_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zvFo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a7e36d-2714-4b9b-9058-d47fc158c799_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f4a7e36d-2714-4b9b-9058-d47fc158c799_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:393697,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a7e36d-2714-4b9b-9058-d47fc158c799_1024x559.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_!zvFo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a7e36d-2714-4b9b-9058-d47fc158c799_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!zvFo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a7e36d-2714-4b9b-9058-d47fc158c799_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!zvFo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a7e36d-2714-4b9b-9058-d47fc158c799_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!zvFo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a7e36d-2714-4b9b-9058-d47fc158c799_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>3.4 Uses vs collaborates-with</h3><p>This distinction is easy to miss, but very valuable.</p><p>Not every &#8220;uses&#8221; relation deserves the same conceptual weight.</p><p>Sometimes one object merely uses another as a tool.</p><p>Sometimes two objects genuinely collaborate to fulfill behavior.</p><p>That difference matters because it changes how central the relationship is to the design.</p><p>A logger may be merely used.</p><p>A checkout service and payment gateway collaborate.</p><p>A pricing service and discount policy collaborate.</p><p>That is a useful mental move even if you never draw it on a UML diagram. It pushes you to ask:</p><p>is this other object just nearby, or is it part of how this object fulfills its responsibility? Some object-oriented traditions treat <code>collaborates-with</code> as one of the few relations that says something intrinsic about an object&#8217;s role, which is one reason this distinction is so valuable in practice.</p><h2>4. Layer 4: Recurring design structures</h2><p>Now we reach the level where many taxonomies become messy.</p><p>Patterns.</p><p>Here is the clean way to place them:</p><p><strong>patterns are recurring arrangements built from simpler relations, not primitive peer categories beside association or inheritance</strong></p><p>That keeps the taxonomy from exploding.</p><p>It also keeps the article honest. When you say &#8220;Strategy,&#8221; &#8220;Decorator,&#8221; or &#8220;Observer,&#8221; you are usually naming a larger arrangement of contracts, links, delegation, and lifecycle decisions, not a new primitive relation.</p><h3>4.1 Strategy</h3><p><strong>Problem:</strong> vary behavior without changing the context.</p><p><strong>Builds on:</strong> interface realization, dependency, delegation.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HIdY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09539217-83a6-4897-b582-7977a298b795_504x916.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HIdY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09539217-83a6-4897-b582-7977a298b795_504x916.png 424w, https://substackcdn.com/image/fetch/$s_!HIdY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09539217-83a6-4897-b582-7977a298b795_504x916.png 848w, https://substackcdn.com/image/fetch/$s_!HIdY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09539217-83a6-4897-b582-7977a298b795_504x916.png 1272w, https://substackcdn.com/image/fetch/$s_!HIdY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09539217-83a6-4897-b582-7977a298b795_504x916.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HIdY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09539217-83a6-4897-b582-7977a298b795_504x916.png" width="346" height="628.8412698412699" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/09539217-83a6-4897-b582-7977a298b795_504x916.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:916,&quot;width&quot;:504,&quot;resizeWidth&quot;:346,&quot;bytes&quot;:46850,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09539217-83a6-4897-b582-7977a298b795_504x916.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_!HIdY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09539217-83a6-4897-b582-7977a298b795_504x916.png 424w, https://substackcdn.com/image/fetch/$s_!HIdY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09539217-83a6-4897-b582-7977a298b795_504x916.png 848w, https://substackcdn.com/image/fetch/$s_!HIdY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09539217-83a6-4897-b582-7977a298b795_504x916.png 1272w, https://substackcdn.com/image/fetch/$s_!HIdY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09539217-83a6-4897-b582-7977a298b795_504x916.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;62dfe0b4-f7fa-4e5f-a948-a698c7f9adce&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface ShippingPricePolicy {
    fun calculateFor(order: Order): Int
}

class ExpressShippingPricePolicy : ShippingPricePolicy {
    override fun calculateFor(order: Order): Int = 1499
}

class ShippingService(
    private val pricingPolicy: ShippingPricePolicy
) {
    fun shippingCostFor(order: Order): Int =
        pricingPolicy.calculateFor(order)
}</code></pre></div><p>What matters here is that the context does not hard-code one behavior. It depends on a contract and hands the work to whichever strategy it receives. That is why Strategy is one of the clearest examples of composition over inheritance in practice.</p><h3>4.2 Decorator</h3><p><strong>Problem:</strong> add responsibilities without changing the outward contract.</p><p><strong>Builds on:</strong> interface realization, composition, delegation.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gkzK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f05e5e1-986f-4333-95d7-a45e89b457e3_1168x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gkzK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f05e5e1-986f-4333-95d7-a45e89b457e3_1168x768.png 424w, https://substackcdn.com/image/fetch/$s_!gkzK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f05e5e1-986f-4333-95d7-a45e89b457e3_1168x768.png 848w, https://substackcdn.com/image/fetch/$s_!gkzK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f05e5e1-986f-4333-95d7-a45e89b457e3_1168x768.png 1272w, https://substackcdn.com/image/fetch/$s_!gkzK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f05e5e1-986f-4333-95d7-a45e89b457e3_1168x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gkzK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f05e5e1-986f-4333-95d7-a45e89b457e3_1168x768.png" width="1168" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f05e5e1-986f-4333-95d7-a45e89b457e3_1168x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1168,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:66880,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f05e5e1-986f-4333-95d7-a45e89b457e3_1168x768.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_!gkzK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f05e5e1-986f-4333-95d7-a45e89b457e3_1168x768.png 424w, https://substackcdn.com/image/fetch/$s_!gkzK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f05e5e1-986f-4333-95d7-a45e89b457e3_1168x768.png 848w, https://substackcdn.com/image/fetch/$s_!gkzK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f05e5e1-986f-4333-95d7-a45e89b457e3_1168x768.png 1272w, https://substackcdn.com/image/fetch/$s_!gkzK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f05e5e1-986f-4333-95d7-a45e89b457e3_1168x768.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;c9bf39da-4770-4ee2-a703-2468ed66f8c3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface ReceiptFormatter {
    fun format(order: Order): String
}

class BasicReceiptFormatter : ReceiptFormatter {
    override fun format(order: Order): String =
        "Order ${order.id.value}"
}

class LoyaltyBannerReceiptFormatter(
    private val inner: ReceiptFormatter
) : ReceiptFormatter {
    override fun format(order: Order): String =
        "Thanks for being a loyal customer\n" + inner.format(order)
}</code></pre></div><p>The decorator keeps the same outward contract, stores a wrapped component through that contract, and delegates work to it while adding behavior around the call. Structurally it resembles Proxy, but the intent is different: Decorator enhances behavior. Proxy controls access.</p><h3>4.3 Proxy</h3><p><strong>Problem:</strong> control access to a real subject or stand in for it.</p><p><strong>Builds on:</strong> interface realization, association, delegation.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!y3BP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9049c6d-5d9e-4366-bfb0-6361c6e41878_812x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!y3BP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9049c6d-5d9e-4366-bfb0-6361c6e41878_812x768.png 424w, https://substackcdn.com/image/fetch/$s_!y3BP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9049c6d-5d9e-4366-bfb0-6361c6e41878_812x768.png 848w, https://substackcdn.com/image/fetch/$s_!y3BP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9049c6d-5d9e-4366-bfb0-6361c6e41878_812x768.png 1272w, https://substackcdn.com/image/fetch/$s_!y3BP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9049c6d-5d9e-4366-bfb0-6361c6e41878_812x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!y3BP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9049c6d-5d9e-4366-bfb0-6361c6e41878_812x768.png" width="812" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b9049c6d-5d9e-4366-bfb0-6361c6e41878_812x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:812,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:58403,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9049c6d-5d9e-4366-bfb0-6361c6e41878_812x768.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_!y3BP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9049c6d-5d9e-4366-bfb0-6361c6e41878_812x768.png 424w, https://substackcdn.com/image/fetch/$s_!y3BP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9049c6d-5d9e-4366-bfb0-6361c6e41878_812x768.png 848w, https://substackcdn.com/image/fetch/$s_!y3BP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9049c6d-5d9e-4366-bfb0-6361c6e41878_812x768.png 1272w, https://substackcdn.com/image/fetch/$s_!y3BP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9049c6d-5d9e-4366-bfb0-6361c6e41878_812x768.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;215abd92-466d-4341-a543-9caad5b07be2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">interface Catalog {
    fun findBySku(sku: String): ProductDescription
}

class InMemoryCatalog(
    products: List&lt;ProductDescription&gt;
) : Catalog {
    private val productsBySku = products.associateBy { it.sku }

    override fun findBySku(sku: String): ProductDescription =
        productsBySku[sku] ?: error("Unknown SKU: $sku")
}

class CachingCatalog(
    private val inner: Catalog
) : Catalog {
    private val cache = mutableMapOf&lt;String, ProductDescription&gt;()

    override fun findBySku(sku: String): ProductDescription =
        cache.getOrPut(sku) { inner.findBySku(sku) }
}</code></pre></div><p>Proxy and Decorator can look structurally similar, but their intent is different. Proxy stands in for the real subject and controls access to it. Decorator adds responsibilities while preserving the same base interface.</p><h3>4.4 Observer</h3><p><strong>Problem:</strong> propagate a change or event to many dependents.</p><p><strong>Builds on:</strong> association or dependency, notification collaboration, one-to-many propagation.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VbEX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe283b4fa-2e8c-4604-93e7-322acbe0dfab_422x648.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VbEX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe283b4fa-2e8c-4604-93e7-322acbe0dfab_422x648.png 424w, https://substackcdn.com/image/fetch/$s_!VbEX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe283b4fa-2e8c-4604-93e7-322acbe0dfab_422x648.png 848w, https://substackcdn.com/image/fetch/$s_!VbEX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe283b4fa-2e8c-4604-93e7-322acbe0dfab_422x648.png 1272w, https://substackcdn.com/image/fetch/$s_!VbEX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe283b4fa-2e8c-4604-93e7-322acbe0dfab_422x648.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VbEX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe283b4fa-2e8c-4604-93e7-322acbe0dfab_422x648.png" width="336" height="515.9431279620853" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e283b4fa-2e8c-4604-93e7-322acbe0dfab_422x648.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:648,&quot;width&quot;:422,&quot;resizeWidth&quot;:336,&quot;bytes&quot;:29196,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe283b4fa-2e8c-4604-93e7-322acbe0dfab_422x648.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_!VbEX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe283b4fa-2e8c-4604-93e7-322acbe0dfab_422x648.png 424w, https://substackcdn.com/image/fetch/$s_!VbEX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe283b4fa-2e8c-4604-93e7-322acbe0dfab_422x648.png 848w, https://substackcdn.com/image/fetch/$s_!VbEX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe283b4fa-2e8c-4604-93e7-322acbe0dfab_422x648.png 1272w, https://substackcdn.com/image/fetch/$s_!VbEX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe283b4fa-2e8c-4604-93e7-322acbe0dfab_422x648.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-cXx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896e86dd-c6e0-4949-a669-c9dbdb321990_1306x534.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-cXx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896e86dd-c6e0-4949-a669-c9dbdb321990_1306x534.png 424w, https://substackcdn.com/image/fetch/$s_!-cXx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896e86dd-c6e0-4949-a669-c9dbdb321990_1306x534.png 848w, https://substackcdn.com/image/fetch/$s_!-cXx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896e86dd-c6e0-4949-a669-c9dbdb321990_1306x534.png 1272w, https://substackcdn.com/image/fetch/$s_!-cXx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896e86dd-c6e0-4949-a669-c9dbdb321990_1306x534.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-cXx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896e86dd-c6e0-4949-a669-c9dbdb321990_1306x534.png" width="1306" height="534" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/896e86dd-c6e0-4949-a669-c9dbdb321990_1306x534.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:534,&quot;width&quot;:1306,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:48367,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896e86dd-c6e0-4949-a669-c9dbdb321990_1306x534.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_!-cXx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896e86dd-c6e0-4949-a669-c9dbdb321990_1306x534.png 424w, https://substackcdn.com/image/fetch/$s_!-cXx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896e86dd-c6e0-4949-a669-c9dbdb321990_1306x534.png 848w, https://substackcdn.com/image/fetch/$s_!-cXx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896e86dd-c6e0-4949-a669-c9dbdb321990_1306x534.png 1272w, https://substackcdn.com/image/fetch/$s_!-cXx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896e86dd-c6e0-4949-a669-c9dbdb321990_1306x534.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;aae9b635-6806-4ccc-90c1-51038685370e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">interface OrderObserver {
    fun onPlaced(order: Order)
}

class ObservableOrder(
    private val idValue: OrderId,
    private val customer: Customer
) {
    private val observers = mutableListOf&lt;OrderObserver&gt;()

    fun subscribe(observer: OrderObserver) {
        observers += observer
    }

    fun place() {
        val order = Order(idValue, customer)
        observers.forEach { it.onPlaced(order) }
    }
}</code></pre></div><p>Observer is easier to understand when you see both pieces:</p><ul><li><p>The static structure: one subject, many observers</p></li><li><p>The dynamic flow: one event, many notifications</p></li></ul><p>That is why a diagram helps here more than a paragraph alone.</p><h3>4.5 Composite</h3><p><strong>Problem:</strong> treat one object and a whole tree of objects uniformly.</p><p><strong>Builds on:</strong> interface realization, recursive association, and often composition.</p><p>A familiar example is packaging:</p><ul><li><p>A box can contain products</p></li><li><p>A box can also contain smaller boxes</p></li><li><p>Both can be treated through the same interface when you calculate total price</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zUqr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe723b718-f2f9-4f1c-8264-ea86eb314061_1012x816.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zUqr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe723b718-f2f9-4f1c-8264-ea86eb314061_1012x816.png 424w, https://substackcdn.com/image/fetch/$s_!zUqr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe723b718-f2f9-4f1c-8264-ea86eb314061_1012x816.png 848w, https://substackcdn.com/image/fetch/$s_!zUqr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe723b718-f2f9-4f1c-8264-ea86eb314061_1012x816.png 1272w, https://substackcdn.com/image/fetch/$s_!zUqr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe723b718-f2f9-4f1c-8264-ea86eb314061_1012x816.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zUqr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe723b718-f2f9-4f1c-8264-ea86eb314061_1012x816.png" width="1012" height="816" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e723b718-f2f9-4f1c-8264-ea86eb314061_1012x816.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:816,&quot;width&quot;:1012,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:71364,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe723b718-f2f9-4f1c-8264-ea86eb314061_1012x816.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_!zUqr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe723b718-f2f9-4f1c-8264-ea86eb314061_1012x816.png 424w, https://substackcdn.com/image/fetch/$s_!zUqr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe723b718-f2f9-4f1c-8264-ea86eb314061_1012x816.png 848w, https://substackcdn.com/image/fetch/$s_!zUqr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe723b718-f2f9-4f1c-8264-ea86eb314061_1012x816.png 1272w, https://substackcdn.com/image/fetch/$s_!zUqr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe723b718-f2f9-4f1c-8264-ea86eb314061_1012x816.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;6e5f7e7f-8040-4acd-948a-8b3036381a84&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">interface PackageComponent {
    fun totalPriceInCents(): Int
}

class ProductItem(
    private val priceInCents: Int
) : PackageComponent {
    override fun totalPriceInCents(): Int = priceInCents
}

class PackageBox : PackageComponent {
    private val children = mutableListOf&lt;PackageComponent&gt;()

    fun add(component: PackageComponent) {
        children += component
    }

    override fun totalPriceInCents(): Int =
        children.sumOf { it.totalPriceInCents() }
}</code></pre></div><p>What matters here is the recursive structure:</p><ul><li><p>Leaves and containers share one common interface</p></li><li><p>The container stores children through that same interface</p></li><li><p>The client can treat one object and a whole tree in the same way<br></p></li></ul><p>This is also why Composite is easy to confuse with composition. Composite is a <strong>pattern</strong> that often uses composition. Composition is a <strong>relationship</strong> about ownership and lifecycle. They are related, but not the same thing.</p><blockquote><p>Patterns are not missing relationship types. They are recurring structures built from simpler ones.</p></blockquote><h2>5. The confusion that wastes the most time: composition vs composition over inheritance</h2><p>These are not the same thing.</p><p>They answer different questions.</p><p>And a lot of confusion disappears once you separate them.</p><h3>5.1 UML composition</h3><p>This is the relationship we covered earlier:</p><ul><li><p>Strong whole-part</p></li><li><p>Lifecycle ownership</p></li><li><p>One whole responsible for its parts</p></li></ul><p>Example:</p><ul><li><p><code>Order</code> composed of <code>OrderLine</code></p></li></ul><p>That is a structural modelling idea.</p><p>It tells you something about ownership, containment, and lifecycle.</p><h3>5.2 Composition over inheritance</h3><p>This is not a UML relation.</p><p>It is a design guideline.</p><p>It means:</p><p>Prefer assembling behavior from collaborating objects instead of subclassing just to reuse behavior.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;kotlin&quot;,&quot;nodeId&quot;:&quot;60dd8387-dcbb-4420-8768-ea05aeda2d0c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-kotlin">interface DiscountPolicy {
    fun applyTo(totalInCents: Int): Int
}

class NoDiscountPolicy : DiscountPolicy {
    override fun applyTo(totalInCents: Int): Int = totalInCents
}

class SeasonalDiscountPolicy : DiscountPolicy {
    override fun applyTo(totalInCents: Int): Int = totalInCents - 1000
}

class PricingService(
    private val discountPolicy: DiscountPolicy
) {
    fun finalPrice(totalInCents: Int): Int =
        discountPolicy.applyTo(totalInCents)
}</code></pre></div><p>Here the service is not a whole made of parts in the UML sense.</p><p>Instead, the design is assembling behavior through collaboration.</p><p>Same word root. Different question.</p><blockquote><p>UML composition asks: who owns the part?<br>Composition over inheritance asks: how should behavior be assembled?</p></blockquote><h3>5.3 Why composition over inheritance is often the safer default</h3><p>This is the part that usually gets skipped too quickly, and it is the part that gives the guideline its practical force.</p><p>Inheritance is not bad.</p><p>But it is very easy to reach for it too early because it feels tidy at first.</p><p>Two classes share some behavior.<br>So you extract a base class.<br>Now the duplication is gone and the hierarchy looks cleaner.</p><p>At that point, inheritance feels like a win.</p><p>The problems usually show up later.</p><p>A subclass cannot freely stop being what the parent says it is.<br>It cannot quietly narrow the parent contract just because that would be convenient.<br>If it overrides behavior, it is still expected to remain compatible with what callers already expect from the base type.</p><p>That is where the pressure starts.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!j2te!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4412d3e0-4046-4818-8457-5dc4f288f0ed_1408x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!j2te!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4412d3e0-4046-4818-8457-5dc4f288f0ed_1408x768.png 424w, https://substackcdn.com/image/fetch/$s_!j2te!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4412d3e0-4046-4818-8457-5dc4f288f0ed_1408x768.png 848w, https://substackcdn.com/image/fetch/$s_!j2te!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4412d3e0-4046-4818-8457-5dc4f288f0ed_1408x768.png 1272w, https://substackcdn.com/image/fetch/$s_!j2te!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4412d3e0-4046-4818-8457-5dc4f288f0ed_1408x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!j2te!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4412d3e0-4046-4818-8457-5dc4f288f0ed_1408x768.png" width="1408" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4412d3e0-4046-4818-8457-5dc4f288f0ed_1408x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1169805,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4412d3e0-4046-4818-8457-5dc4f288f0ed_1408x768.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_!j2te!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4412d3e0-4046-4818-8457-5dc4f288f0ed_1408x768.png 424w, https://substackcdn.com/image/fetch/$s_!j2te!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4412d3e0-4046-4818-8457-5dc4f288f0ed_1408x768.png 848w, https://substackcdn.com/image/fetch/$s_!j2te!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4412d3e0-4046-4818-8457-5dc4f288f0ed_1408x768.png 1272w, https://substackcdn.com/image/fetch/$s_!j2te!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4412d3e0-4046-4818-8457-5dc4f288f0ed_1408x768.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The subclass is no longer just reusing code.<br>It is also carrying the meaning, assumptions, and constraints of the parent.</p><p>That creates a few common problems:</p><ul><li><p>Subclasses become tightly coupled to superclasses</p></li><li><p>Internal details of the parent start leaking into child classes</p></li><li><p>Changes in the parent can ripple into several descendants</p></li><li><p>One inheritance tree is often forced to represent several variation dimensions at once</p></li></ul><p>And that last point is where designs often become brittle.</p><p>Maybe one hierarchy started as a clean taxonomy.<br>Later it also needs to express discount rules.<br>Then output format.<br>Then delivery policy.<br>Then validation differences.</p><p>Now the hierarchy is doing too many jobs at once.</p><p>Composition helps because it keeps those decisions more local.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Y4Qp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a5b3ce0-46d7-491c-a91d-a42ffdf5881f_1408x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Y4Qp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a5b3ce0-46d7-491c-a91d-a42ffdf5881f_1408x768.png 424w, https://substackcdn.com/image/fetch/$s_!Y4Qp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a5b3ce0-46d7-491c-a91d-a42ffdf5881f_1408x768.png 848w, https://substackcdn.com/image/fetch/$s_!Y4Qp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a5b3ce0-46d7-491c-a91d-a42ffdf5881f_1408x768.png 1272w, https://substackcdn.com/image/fetch/$s_!Y4Qp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a5b3ce0-46d7-491c-a91d-a42ffdf5881f_1408x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Y4Qp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a5b3ce0-46d7-491c-a91d-a42ffdf5881f_1408x768.png" width="1408" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8a5b3ce0-46d7-491c-a91d-a42ffdf5881f_1408x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1022263,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a5b3ce0-46d7-491c-a91d-a42ffdf5881f_1408x768.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_!Y4Qp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a5b3ce0-46d7-491c-a91d-a42ffdf5881f_1408x768.png 424w, https://substackcdn.com/image/fetch/$s_!Y4Qp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a5b3ce0-46d7-491c-a91d-a42ffdf5881f_1408x768.png 848w, https://substackcdn.com/image/fetch/$s_!Y4Qp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a5b3ce0-46d7-491c-a91d-a42ffdf5881f_1408x768.png 1272w, https://substackcdn.com/image/fetch/$s_!Y4Qp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a5b3ce0-46d7-491c-a91d-a42ffdf5881f_1408x768.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A service can delegate pricing to a pricing policy.<br>A formatter can wrap another formatter.<br>A checkout flow can collaborate with a payment gateway.<br>A class can satisfy a contract without inheriting implementation from a parent.</p><p>That usually gives you a flatter and more flexible design:</p><ul><li><p>Behavior can vary independently</p></li><li><p>Responsibilities are easier to isolate</p></li><li><p>Changes stay more local</p></li><li><p>One variation does not automatically force a redesign of the whole hierarchy</p></li><li><p>Several dimensions of change do not immediately explode into subclass combinations</p></li></ul><p>So the real takeaway <strong>is not</strong>:</p><p>&#8220;inheritance is wrong.&#8221;</p><p>It is:</p><p><strong>Use inheritance when you truly have a stable kind-of relationship and safe substitutability. Use composition when what you really need is flexible behavior assembly.</strong> Composition over inheritance is recommended precisely because inheritance brings interface pressure, encapsulation leakage, tight coupling, and the risk of parallel hierarchy explosion when several variation dimensions are forced into one tree.</p><p>Another time I use inheritance is when I&#8217;m refactoring; s<strong>ometimes it&#8217;s a useful intermediate step</strong> that will eventually be removed once the refactoring is complete, but it&#8217;s a step I take to start refactoring what I want. Examples of this include when you have to use legacy code techniques such as &#8220;Subclass and Override Method&#8221;.</p><h2>6. A practical decision guide</h2><p>When you are not sure which relationship you are looking at, do not start by memorizing notation.</p><p>Start by asking the right question.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!z4FQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e3a6abe-2101-475b-b5f3-d14e3639602b_3162x5515.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z4FQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e3a6abe-2101-475b-b5f3-d14e3639602b_3162x5515.png 424w, https://substackcdn.com/image/fetch/$s_!z4FQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e3a6abe-2101-475b-b5f3-d14e3639602b_3162x5515.png 848w, https://substackcdn.com/image/fetch/$s_!z4FQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e3a6abe-2101-475b-b5f3-d14e3639602b_3162x5515.png 1272w, https://substackcdn.com/image/fetch/$s_!z4FQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e3a6abe-2101-475b-b5f3-d14e3639602b_3162x5515.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!z4FQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e3a6abe-2101-475b-b5f3-d14e3639602b_3162x5515.png" width="1456" height="2539" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e3a6abe-2101-475b-b5f3-d14e3639602b_3162x5515.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2539,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:640222,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e3a6abe-2101-475b-b5f3-d14e3639602b_3162x5515.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_!z4FQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e3a6abe-2101-475b-b5f3-d14e3639602b_3162x5515.png 424w, https://substackcdn.com/image/fetch/$s_!z4FQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e3a6abe-2101-475b-b5f3-d14e3639602b_3162x5515.png 848w, https://substackcdn.com/image/fetch/$s_!z4FQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e3a6abe-2101-475b-b5f3-d14e3639602b_3162x5515.png 1272w, https://substackcdn.com/image/fetch/$s_!z4FQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e3a6abe-2101-475b-b5f3-d14e3639602b_3162x5515.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The goal is not to turn judgment into a flowchart. The goal is to prevent category mistakes.</p><p>Most mistakes here happen because people pick a label before they decide which layer they are working in.</p><h2>7. Conclusion</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kXv0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21af7328-9669-4b20-89f6-4690df1b90b0_3600x2655.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kXv0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21af7328-9669-4b20-89f6-4690df1b90b0_3600x2655.png 424w, https://substackcdn.com/image/fetch/$s_!kXv0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21af7328-9669-4b20-89f6-4690df1b90b0_3600x2655.png 848w, https://substackcdn.com/image/fetch/$s_!kXv0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21af7328-9669-4b20-89f6-4690df1b90b0_3600x2655.png 1272w, https://substackcdn.com/image/fetch/$s_!kXv0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21af7328-9669-4b20-89f6-4690df1b90b0_3600x2655.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kXv0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21af7328-9669-4b20-89f6-4690df1b90b0_3600x2655.png" width="1456" height="1074" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/21af7328-9669-4b20-89f6-4690df1b90b0_3600x2655.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1074,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1652838,&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://emmanuelvalverderamos.substack.com/i/191836522?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21af7328-9669-4b20-89f6-4690df1b90b0_3600x2655.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_!kXv0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21af7328-9669-4b20-89f6-4690df1b90b0_3600x2655.png 424w, https://substackcdn.com/image/fetch/$s_!kXv0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21af7328-9669-4b20-89f6-4690df1b90b0_3600x2655.png 848w, https://substackcdn.com/image/fetch/$s_!kXv0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21af7328-9669-4b20-89f6-4690df1b90b0_3600x2655.png 1272w, https://substackcdn.com/image/fetch/$s_!kXv0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21af7328-9669-4b20-89f6-4690df1b90b0_3600x2655.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Most confusion about object relationships is not caused by impossible definitions.</p><p>It is caused by flattening several different kinds of design decision into one list.</p><p>Once you separate the layers, the topic becomes much calmer:</p><p>structural links tell you who is related to whom</p><p>type and contract relations tell you what kind of thing something is and what it promises</p><p>usage and collaboration relations tell you who needs whom to get work done</p><p>recurring design structures tell you how simpler relations are repeatedly combined in good designs</p><p>That layered view does something important: it helps you choose the right question before you choose the label.</p><ul><li><p>If you are describing a stable link, you are probably in the structural layer.</p></li><li><p>If you are describing kind-of meaning or contract conformance, you are in the type layer.</p></li><li><p>If you are describing temporary use or behavioral handoff, you are in the collaboration layer.</p></li><li><p>If you are describing a larger recurring arrangement such as Strategy, Decorator, Proxy, Observer, or Composite, you are no longer dealing with a primitive relation at all. You are looking at a design structure built from several simpler ones.</p></li></ul><p>That is also why some distinctions deserve more weight than others.</p><p>Aggregation is worth recognizing, but rarely worth centering.<br>Composition matters because ownership and lifecycle matter.<br>Subtyping and subclassing must be kept separate because substitutability and implementation reuse are not the same decision.<br>And composition over inheritance matters because it reminds us that flexible behavior is often assembled more safely through contracts, delegation, and collaboration than through deep hierarchies.</p><p>In the end, the problem was never the words themselves.</p><p>The problem was using one flat vocabulary for several different kinds of meaning.</p><p>Once you stop doing that, object relationships stop feeling slippery. The arrows become easier to read. The code becomes easier to model. And the design decisions behind both become easier to explain.</p><blockquote><p>The real skill is not memorizing the notation. The real skill is noticing which layer you are in.</p></blockquote><h2>References</h2><p>This article is grounded mainly in these uploaded sources:</p><ul><li><p><strong>David West, </strong><em><strong>Object Thinking</strong></em><br>Especially useful for the distinction between intrinsic and situational relationships, and for the importance of <code>is-a-kind-of</code> and <code>collaborates-with</code>.</p></li><li><p><strong>Martin Fowler, </strong><em><strong>Analysis Patterns</strong></em><br>Especially useful for association vs mapping, generalization, subtyping vs subclassing, multiple classification, and dynamic classification.</p></li><li><p><strong>Craig Larman, </strong><em><strong>Applying UML and Patterns</strong></em><br>Especially useful for qualified association, association class, visibility, aggregation, composition, and UML notation for interface realization.</p></li><li><p><strong>Alexander Shvets, </strong><em><strong>Dive Into Design Patterns</strong></em><br>Useful for the practical framing of object relations, the rationale behind favoring composition over inheritance, and the structural understanding of Decorator, Proxy, Strategy, Observer, and Composite.</p></li></ul>]]></content:encoded></item><item><title><![CDATA[The Productivity mirage]]></title><description><![CDATA[Large Language Models can make the development subsystem faster. That does not mean the organization became more productive.]]></description><link>https://emmanuelvalverderamos.substack.com/p/the-productivity-mirage</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/the-productivity-mirage</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Thu, 11 Jun 2026 05:01:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!DDQm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DDQm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DDQm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!DDQm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!DDQm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!DDQm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DDQm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1039577,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.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_!DDQm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!DDQm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!DDQm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!DDQm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7571051f-cf21-4d9b-bab2-7e51c7739ac9_1024x558.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Imagine a restaurant that buys a miraculous new oven.</p><p>Before the oven, the kitchen could prepare ten dishes per hour. After the oven, it can prepare thirteen, twenty, maybe one hundred. The kitchen manager is thrilled. The board is shown a clean chart: dishes produced per hour went up. Someone writes a confident internal memo saying the restaurant has become dramatically more productive.</p><p>Then you walk into the dining room and the story changes. Some tables are empty. Some dishes were never ordered. Some plates arrive faster than waiters can serve them. Some food looks perfect but has to be sent back after the first bite. The fridge is filling with leftovers. The menu is longer, but customers are not happier. Revenue has not improved. Waste has increased.</p><p>The kitchen became faster. The restaurant did not become more productive.</p><p>That is the mistake many organizations are about to make with <em>Large Language Models</em> (<em>LLMs</em>) and software development. They will look at the subsystem where output is easiest to count, the development team, and confuse local acceleration with organizational productivity.</p><p>But an organization is not a code generator. A product is not a pull request factory. A business is not a backlog-burning machine. An organization becomes more productive when it turns investment into useful outcomes with acceptable cost, risk, quality, flow, maintainability, and learning. If <em>LLM</em>-based AI helps a team generate more software that nobody uses, nobody wants, nobody deletes, and everyone has to maintain, the organization has not become more productive. It has become better at manufacturing liabilities.</p><div class="callout-block" data-callout="true"><p><strong>Every time I say AI on this article I&#8217;m talking about LLM and agents, and also MCP, because all of this elements ARE NOT AI. But everybody call this AI</strong></p></div><h2>TL;DR</h2><p><em>LLM</em>-based AI may increase coding activity and some upstream throughput metrics. That does not prove organizational productivity. Productivity is not measured by how much software enters the system. It is measured by how much investment returns as useful, adopted, maintainable value.</p><p>Efficiency, effectiveness, and productivity are not the same thing. A development team can become more efficient at producing features while the organization becomes less effective at creating value. If productivity is defined as &#8220;more features,&#8221; then more features will look like success. But the organization does not exist to maximize feature count. It exists to generate return from investment.</p><p>The development team is a subsystem. Optimizing that subsystem can overload discovery, prioritization, review, testing, deployment, operations, support, maintenance, and customer adoption. If the organization cannot show better outcomes, better flow, better quality, lower risk, lower cost of ownership, or higher <em>Return on Investment</em> (<em>ROI</em>), it should not claim that AI made it more productive.</p><p>A feature is an investment only when there is a plausible path to return. If it does not create value, reduce risk, increase learning, improve flow, or create useful optionality, it is not an investment. It is cost.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Article index</h2><ol><li><p>Productivity, efficiency, and effectiveness are not the same thing</p></li><li><p>The productivity claim is using the wrong boundary</p></li><li><p>Software is an investment before it is value</p></li><li><p>Output, outcome, and the ladder of evidence</p></li><li><p>More output can create more economic burden</p></li><li><p>Flow decides whether local speed becomes system throughput</p></li><li><p>Systems thinking: the structure produces the behavior</p></li><li><p>What the evidence actually says</p></li><li><p>The AI-assisted feature equation</p></li><li><p>What organizations should measure instead</p></li><li><p>Conclusions</p></li><li><p>References</p></li></ol><h2>1. Productivity, efficiency, and effectiveness are not the same thing</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PX7w!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb28ae4c2-0902-4c4b-a241-3c7e631e91cb_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PX7w!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb28ae4c2-0902-4c4b-a241-3c7e631e91cb_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!PX7w!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb28ae4c2-0902-4c4b-a241-3c7e631e91cb_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!PX7w!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb28ae4c2-0902-4c4b-a241-3c7e631e91cb_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!PX7w!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb28ae4c2-0902-4c4b-a241-3c7e631e91cb_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PX7w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb28ae4c2-0902-4c4b-a241-3c7e631e91cb_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b28ae4c2-0902-4c4b-a241-3c7e631e91cb_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:825804,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb28ae4c2-0902-4c4b-a241-3c7e631e91cb_1024x558.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_!PX7w!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb28ae4c2-0902-4c4b-a241-3c7e631e91cb_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!PX7w!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb28ae4c2-0902-4c4b-a241-3c7e631e91cb_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!PX7w!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb28ae4c2-0902-4c4b-a241-3c7e631e91cb_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!PX7w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb28ae4c2-0902-4c4b-a241-3c7e631e91cb_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Before discussing whether <em>Large Language Models</em> (<em>LLMs</em>) make organizations more productive, we need to slow down and ask a more basic question: productive at what?</p><p>Efficiency is about the relationship between resources consumed and output produced. If a team creates the same output with less time, less effort, or less cost, it has become more efficient at that activity. A code assistant may improve efficiency in some parts of development if it helps produce drafts, tests, migrations, documentation, or repetitive changes with less effort.</p><p>Effectiveness is different. Effectiveness asks whether the thing we produced achieved the intended purpose. A team can be efficient at building features and still ineffective at improving the product. A feature delivered quickly but ignored by users is not an effective business outcome. It is merely an efficiently produced cost.</p><p>Productivity is the most dangerous word because it depends on what we decide to count. A basic productivity ratio compares output with input, but the ratio becomes misleading when we choose the wrong output. If productivity means &#8220;features delivered per month,&#8221; then producing more features makes a team look more productive. That definition may be useful inside a narrow development dashboard, but it is not enough for an organization.</p><p>The organization does not exist to maximize feature count. It exists to turn investment into valuable return. That return may appear as revenue, adoption, retention, lower support load, lower operational cost, reduced risk, faster learning, or stronger strategic positioning. More features only matter if they contribute to that return.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!B_By!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26696ec-87dc-4597-a2d8-bd4ae4d92b6c_1792x700.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B_By!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26696ec-87dc-4597-a2d8-bd4ae4d92b6c_1792x700.png 424w, https://substackcdn.com/image/fetch/$s_!B_By!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26696ec-87dc-4597-a2d8-bd4ae4d92b6c_1792x700.png 848w, https://substackcdn.com/image/fetch/$s_!B_By!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26696ec-87dc-4597-a2d8-bd4ae4d92b6c_1792x700.png 1272w, https://substackcdn.com/image/fetch/$s_!B_By!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26696ec-87dc-4597-a2d8-bd4ae4d92b6c_1792x700.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B_By!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26696ec-87dc-4597-a2d8-bd4ae4d92b6c_1792x700.png" width="1456" height="569" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f26696ec-87dc-4597-a2d8-bd4ae4d92b6c_1792x700.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:569,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:100558,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26696ec-87dc-4597-a2d8-bd4ae4d92b6c_1792x700.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!B_By!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26696ec-87dc-4597-a2d8-bd4ae4d92b6c_1792x700.png 424w, https://substackcdn.com/image/fetch/$s_!B_By!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26696ec-87dc-4597-a2d8-bd4ae4d92b6c_1792x700.png 848w, https://substackcdn.com/image/fetch/$s_!B_By!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26696ec-87dc-4597-a2d8-bd4ae4d92b6c_1792x700.png 1272w, https://substackcdn.com/image/fetch/$s_!B_By!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26696ec-87dc-4597-a2d8-bd4ae4d92b6c_1792x700.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is why productivity is misleading when we do not name the goal. If we evaluate productivity as &#8220;more functionality shipped,&#8221; we can always make the number go up. We can generate more code, open more pull requests, merge more changes, and release more product surface area. But if those features do not increase adoption, traction, learning, cost reduction, or <em>ROI</em>, the organization has not become more productive. It has become more efficient at creating cost.</p><p>Productivity is not neutral. It always hides a theory of value.</p><p>So the real question is not whether <em>LLMs</em> help us produce more software. The real question is whether they help the organization produce more valuable outcomes per unit of investment. If we do not make that distinction, we are not measuring productivity. We are measuring movement and hoping it means progress.</p><h2>2. The productivity claim is using the wrong boundary</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pQBH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41893280-73de-4c34-80cd-d1a8c4369a4d_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pQBH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41893280-73de-4c34-80cd-d1a8c4369a4d_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!pQBH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41893280-73de-4c34-80cd-d1a8c4369a4d_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!pQBH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41893280-73de-4c34-80cd-d1a8c4369a4d_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!pQBH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41893280-73de-4c34-80cd-d1a8c4369a4d_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pQBH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41893280-73de-4c34-80cd-d1a8c4369a4d_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/41893280-73de-4c34-80cd-d1a8c4369a4d_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:842935,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41893280-73de-4c34-80cd-d1a8c4369a4d_1024x558.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_!pQBH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41893280-73de-4c34-80cd-d1a8c4369a4d_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!pQBH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41893280-73de-4c34-80cd-d1a8c4369a4d_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!pQBH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41893280-73de-4c34-80cd-d1a8c4369a4d_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!pQBH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41893280-73de-4c34-80cd-d1a8c4369a4d_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The claim usually sounds practical.</p><ul><li><p>&#8220;We are more productive because developers complete more tickets.&#8221;</p></li><li><p>&#8220;We are more productive because pull requests are created faster.&#8221;</p></li><li><p>&#8220;We are more productive because AI helps us ship more features.&#8221;</p></li><li><p>&#8220;We are more productive because our engineers write more code with fewer people.&#8221;</p></li></ul><p>Those claims may describe local activity, but they do not prove organizational productivity. They measure what happens inside one part of the system. They do not prove that the whole system is turning investment into value.</p><p>The development team is a subsystem. A very important one, but still a subsystem. It receives signals from product strategy, discovery, customer research, sales pressure, incidents, compliance needs, executive urgency, architecture constraints, and operational reality. It produces software changes that must travel through review, testing, deployment, release, adoption, support, learning, maintenance, and sometimes deletion.</p><p>A simplified value stream looks like this:</p><pre><code><code>Idea
-&gt; Discovery
-&gt; Prioritization
-&gt; Design
-&gt; Development
-&gt; Review
-&gt; Testing
-&gt; Deployment
-&gt; Release
-&gt; Adoption
-&gt; Support
-&gt; Learning
-&gt; Evolution or deletion
-&gt; Economic return</code></code></pre><p>Development matters, but it is not the whole journey. In many organizations, development is not even the constraint. The constraint may be unclear strategy, slow decision-making, too much <em>Work in Progress</em> (<em>WIP</em>), weak product discovery, overloaded review, brittle tests, manual regression, release fear, poor observability, high support load, or inability to delete.</p><p>If the constraint is not code generation, making code generation faster will not necessarily increase organizational throughput. It may simply create more inventory before the real bottleneck.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dD6S!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa17840b2-157e-4899-b828-4aba57121ed8_5730x684.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dD6S!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa17840b2-157e-4899-b828-4aba57121ed8_5730x684.png 424w, https://substackcdn.com/image/fetch/$s_!dD6S!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa17840b2-157e-4899-b828-4aba57121ed8_5730x684.png 848w, https://substackcdn.com/image/fetch/$s_!dD6S!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa17840b2-157e-4899-b828-4aba57121ed8_5730x684.png 1272w, https://substackcdn.com/image/fetch/$s_!dD6S!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa17840b2-157e-4899-b828-4aba57121ed8_5730x684.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dD6S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa17840b2-157e-4899-b828-4aba57121ed8_5730x684.png" width="1456" height="174" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a17840b2-157e-4899-b828-4aba57121ed8_5730x684.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:174,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:160963,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa17840b2-157e-4899-b828-4aba57121ed8_5730x684.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_!dD6S!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa17840b2-157e-4899-b828-4aba57121ed8_5730x684.png 424w, https://substackcdn.com/image/fetch/$s_!dD6S!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa17840b2-157e-4899-b828-4aba57121ed8_5730x684.png 848w, https://substackcdn.com/image/fetch/$s_!dD6S!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa17840b2-157e-4899-b828-4aba57121ed8_5730x684.png 1272w, https://substackcdn.com/image/fetch/$s_!dD6S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa17840b2-157e-4899-b828-4aba57121ed8_5730x684.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>That inventory might look like pull requests waiting for review. It might look like half-finished tickets. It might look like features shipped but not adopted. It might look like code paths that nobody understands six months later. It might look like support burden, incident risk, cloud cost, test maintenance, documentation drift, or product surface area that users must navigate.</p><p>Software inventory is dangerous because it does not always look like inventory. It does not sit on a shelf with dust on it. It hides inside the codebase, the product interface, the test suite, the deployment pipeline, the support queue, and the minds of the people who must remember why it exists.</p><p>So the first question is not whether developers can move faster with <em>LLMs</em>. Sometimes they can. The first question is whether faster movement inside the development subsystem improves the larger economic system.</p><h2>3. Software is an investment before it is value</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WJYV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb2921c9-7a18-4bba-8f87-ef17c273797d_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WJYV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb2921c9-7a18-4bba-8f87-ef17c273797d_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!WJYV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb2921c9-7a18-4bba-8f87-ef17c273797d_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!WJYV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb2921c9-7a18-4bba-8f87-ef17c273797d_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!WJYV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb2921c9-7a18-4bba-8f87-ef17c273797d_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WJYV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb2921c9-7a18-4bba-8f87-ef17c273797d_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fb2921c9-7a18-4bba-8f87-ef17c273797d_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:478175,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb2921c9-7a18-4bba-8f87-ef17c273797d_1024x559.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_!WJYV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb2921c9-7a18-4bba-8f87-ef17c273797d_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!WJYV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb2921c9-7a18-4bba-8f87-ef17c273797d_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!WJYV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb2921c9-7a18-4bba-8f87-ef17c273797d_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!WJYV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb2921c9-7a18-4bba-8f87-ef17c273797d_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A feature is not &#8220;work completed.&#8221; A feature is capital placed at risk in the hope of future benefit.</p><p>Before a feature is adopted, it is not value. It is a bet. It consumes time, money, attention, opportunity, and risk capacity before reality tells us whether it was worth building. That distinction changes the entire conversation.</p><p>The uploaded software-economics chapter frames software as a way to turn money, time, attention, and judgment into capability, then capability into value, and value into return. It also makes a simple but often ignored point: closed tickets and lines of code do not imply profit.</p><p>That means a company should not ask only, &#8220;What can AI help us build faster?&#8221; It should also ask what evidence suggests the feature should exist, what customer behavior would prove that it matters, what the cost of being wrong is, what better alternative is being delayed, how quickly the organization will know whether the idea worked, how easily the feature can be removed, and what it will cost while it exists.</p><p>Those questions are not philosophical decoration. They are software economics.</p><p>An investment needs return. In software, return may appear as revenue, retention, conversion, reduced operational cost, lower support burden, faster learning, reduced risk, stronger strategic positioning, or increased ability to change. <em>ROI</em> is not only about &#8220;money came back immediately.&#8221; It is about whether the value created justifies the investment made, when that return arrives, how likely it is, what was blocked while waiting, and how much freedom remains after the commitment.</p><p>A feature that creates no return is not a weak investment. It is cost.</p><p>This is where <em>LLM</em>-based AI changes the shape of the problem. If AI reduces the apparent cost of producing software, it becomes easier to create more bets. That can be useful when the organization is disciplined about discovery, small slices, fast feedback, adoption measurement, and deletion. It becomes dangerous when the organization already confuses output with value.</p><p>Cheaper production does not create demand. It only lowers the friction required to place more bets.</p><h2>4. Output, outcome, and the ladder of evidence</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5_OJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f418d42-5a23-48bc-af69-5a9fc038ede3_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5_OJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f418d42-5a23-48bc-af69-5a9fc038ede3_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!5_OJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f418d42-5a23-48bc-af69-5a9fc038ede3_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!5_OJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f418d42-5a23-48bc-af69-5a9fc038ede3_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!5_OJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f418d42-5a23-48bc-af69-5a9fc038ede3_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5_OJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f418d42-5a23-48bc-af69-5a9fc038ede3_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3f418d42-5a23-48bc-af69-5a9fc038ede3_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:675478,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f418d42-5a23-48bc-af69-5a9fc038ede3_1024x558.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_!5_OJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f418d42-5a23-48bc-af69-5a9fc038ede3_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!5_OJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f418d42-5a23-48bc-af69-5a9fc038ede3_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!5_OJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f418d42-5a23-48bc-af69-5a9fc038ede3_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!5_OJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f418d42-5a23-48bc-af69-5a9fc038ede3_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A lot of AI productivity arguments collapse because they use the wrong word at the wrong level.</p><p>Output is what the system produces: prompts, generated code, accepted suggestions, commits, pull requests, tickets, features, releases.</p><p>Outcome is the useful change caused by that output: customer adoption, revenue growth, retention improvement, lower support load, reduced operational cost, faster learning, lower risk, or a better ability to change direction.</p><p>Throughput is the rate at which valuable work flows through the whole system. More coding activity is not necessarily more throughput if work piles up in review, testing, deployment, customer adoption, or maintenance.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!P-ex!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfcbb283-6de2-4e69-ad5b-bf93665db480_670x1804.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!P-ex!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfcbb283-6de2-4e69-ad5b-bf93665db480_670x1804.png 424w, https://substackcdn.com/image/fetch/$s_!P-ex!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfcbb283-6de2-4e69-ad5b-bf93665db480_670x1804.png 848w, https://substackcdn.com/image/fetch/$s_!P-ex!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfcbb283-6de2-4e69-ad5b-bf93665db480_670x1804.png 1272w, https://substackcdn.com/image/fetch/$s_!P-ex!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfcbb283-6de2-4e69-ad5b-bf93665db480_670x1804.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!P-ex!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfcbb283-6de2-4e69-ad5b-bf93665db480_670x1804.png" width="670" height="1804" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bfcbb283-6de2-4e69-ad5b-bf93665db480_670x1804.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1804,&quot;width&quot;:670,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:89697,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfcbb283-6de2-4e69-ad5b-bf93665db480_670x1804.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_!P-ex!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfcbb283-6de2-4e69-ad5b-bf93665db480_670x1804.png 424w, https://substackcdn.com/image/fetch/$s_!P-ex!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfcbb283-6de2-4e69-ad5b-bf93665db480_670x1804.png 848w, https://substackcdn.com/image/fetch/$s_!P-ex!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfcbb283-6de2-4e69-ad5b-bf93665db480_670x1804.png 1272w, https://substackcdn.com/image/fetch/$s_!P-ex!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfcbb283-6de2-4e69-ad5b-bf93665db480_670x1804.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>The higher we climb, the harder the productivity claim becomes. Generated code is easy to count. Accepted suggestions are easy to count. Pull requests are easy to count. Adoption is harder. Retention is harder. Economic return is harder. Deletion discipline is harder.</p><p>That is why organizations often stop measuring too early.</p><p>They measure activity and call it productivity. They measure output and call it value. They measure local speed and call it business improvement.</p><p>A healthier measurement ladder would separate the layers:</p><ul><li><p><strong>Activity metrics</strong>: prompts, generated code, accepted suggestions, commit</p></li><li><p><strong>Engineering throughput metrics</strong>: pull requests, merged changes, cycle time, lead time, deployments</p></li><li><p><strong>Product and business metrics</strong>: adoption, retention, conversion, revenue, cost reduction, support reduction</p></li><li><p><strong>Economic metrics</strong>: ROI, opportunity cost, cost of delay, cost of carry, maintenance cost, risk, liquidity, optionality</p></li></ul><p>The mistake is not measuring activity. Activity can be useful diagnostic data. The mistake is stopping there. A pulse tells you a body is alive, but it does not tell you whether it is healthy, learning, recovering, or winning a marathon.</p><p>The same is true for software organizations.</p><h2>5. More output can create more economic burden</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gdAp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27cb32d8-17e4-42a2-8362-208efb3a7261_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gdAp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27cb32d8-17e4-42a2-8362-208efb3a7261_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!gdAp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27cb32d8-17e4-42a2-8362-208efb3a7261_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!gdAp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27cb32d8-17e4-42a2-8362-208efb3a7261_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!gdAp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27cb32d8-17e4-42a2-8362-208efb3a7261_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gdAp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27cb32d8-17e4-42a2-8362-208efb3a7261_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/27cb32d8-17e4-42a2-8362-208efb3a7261_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:884692,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27cb32d8-17e4-42a2-8362-208efb3a7261_1024x558.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_!gdAp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27cb32d8-17e4-42a2-8362-208efb3a7261_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!gdAp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27cb32d8-17e4-42a2-8362-208efb3a7261_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!gdAp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27cb32d8-17e4-42a2-8362-208efb3a7261_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!gdAp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27cb32d8-17e4-42a2-8362-208efb3a7261_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>More software is not only more capability. More software is also more surface area.</p><p>Every feature that remains in a product has ongoing cost. It must be understood, tested, secured, supported, documented, migrated, monitored, and considered during future changes. The uploaded software-economics chapter says more software is also more burden, and that a feature must justify both its initial cost and its continuing cost of existence. It also defines risk as the probability and consequence of being wrong, which is essential when the wrong feature can become a long-lived cost.</p><p>This ongoing burden is close to what Eduardo Ferro calls the basal cost of software: the cost generated by software simply existing. A feature nobody uses may still increase onboarding friction, support effort, test complexity, deployment risk, security surface, and cognitive load. It may not create visible revenue, but it keeps sending invoices to the organization. Thoughtworks&#8217; discussion of the concept summarizes it as the cost of features beyond initial development, including maintenance and onboarding until the feature is dead. (<a href="https://www.eferro.net/2021/02/basal-cost-of-software.html">Basal cost of software by Edu Ferro</a>)</p><p>That is why deletion is not housekeeping. Deletion is an economic practice.</p><p>If an organization proudly says, &#8220;AI helped us ship 100 features instead of 10,&#8221; the first question should be: &#8220;How many did customers adopt?&#8221; The second should be: &#8220;How many created measurable value?&#8221; The third should be: &#8220;How many did you remove when they did not?&#8221;</p><p>If the answer to the third question is zero, the organization may not have improved productivity. It may have expanded its cost base.</p><p>There are three economic terms that make this clearer.</p><p>Opportunity cost is the value of the best alternative you gave up. When a team builds low-value features, the cost is not only the effort spent on those features. It is also the onboarding problem not fixed, the incident rate not reduced, the product complexity not simplified, the customer research not done, the old feature not deleted, and the bottleneck not addressed.</p><p>Cost of carry is the cost of holding unfinished or unvalidated work. In software, work in progress consumes attention, coordination, context, review capacity, decision capacity, and risk capacity. Branches, tickets, pull requests, unreleased features, partial migrations, and ambiguous decisions all carry cost while they wait.</p><p>Cost of delay is the economic loss caused by receiving value later than we could have. If <em>LLMs</em> help teams start more work but review, testing, release, or adoption becomes slower, the organization can celebrate local acceleration while increasing economic delay.</p><p>This is the trap. AI can make it easier to produce more software, but if that software does not become useful value, the organization has not created more productivity. It has created more cost, faster.</p><h2>6. Flow decides whether local speed becomes system throughput</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PeJD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1e3a51b-2a8a-4747-8aad-6b4d01ebf953_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PeJD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1e3a51b-2a8a-4747-8aad-6b4d01ebf953_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!PeJD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1e3a51b-2a8a-4747-8aad-6b4d01ebf953_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!PeJD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1e3a51b-2a8a-4747-8aad-6b4d01ebf953_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!PeJD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1e3a51b-2a8a-4747-8aad-6b4d01ebf953_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PeJD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1e3a51b-2a8a-4747-8aad-6b4d01ebf953_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f1e3a51b-2a8a-4747-8aad-6b4d01ebf953_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:881857,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1e3a51b-2a8a-4747-8aad-6b4d01ebf953_1024x559.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_!PeJD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1e3a51b-2a8a-4747-8aad-6b4d01ebf953_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!PeJD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1e3a51b-2a8a-4747-8aad-6b4d01ebf953_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!PeJD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1e3a51b-2a8a-4747-8aad-6b4d01ebf953_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!PeJD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1e3a51b-2a8a-4747-8aad-6b4d01ebf953_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Lean thinking is allergic to the confusion between activity and value.</p><p>A busy system is not necessarily a flowing system. A fully utilized system is often a clogged system. A team that starts many things can look committed while becoming slow, brittle, and unable to respond.</p><p>The uploaded software-economics chapter puts this plainly: organizations often assume that the healthiest system is the one where everyone is busy, but a system optimized for utilization can become congested, overloaded, and slow. Activity rises while delivery suffers. It also frames liquidity as the ability to redirect effort, attention, knowledge, and investment toward the most valuable next move.</p><p>This matters because <em>LLM</em>-based AI can increase the amount of work entering the delivery system. More generated code. More branches. More pull requests. More suggested changes. More features asking to exist. If the rest of the value stream cannot absorb that work, queues grow.</p><p>A queue is not a harmless waiting room. It is a place where investment is trapped.</p><p>Pull requests waiting for review are trapped investment. Features waiting for release are trapped investment. Product decisions waiting for evidence are trapped investment. Code deployed behind flags but never evaluated is trapped investment. Every queue delays learning, delays return, and increases the chance that the work becomes stale before it becomes valuable.</p><p>This is where <em>Theory of Constraints</em> (<em>TOC</em>) gives us a clean rule: improving a non-constraint does not improve the whole system. If review is the constraint, generating more code creates a review queue. If product discovery is the constraint, generating more features creates more wrong things faster. If deployment is the constraint, merging more code increases unreleased inventory. If customer adoption is the constraint, shipping more features increases product noise.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MN76!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406d0a9f-3f59-4c9a-adc7-53bccf476d08_3536x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MN76!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406d0a9f-3f59-4c9a-adc7-53bccf476d08_3536x188.png 424w, https://substackcdn.com/image/fetch/$s_!MN76!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406d0a9f-3f59-4c9a-adc7-53bccf476d08_3536x188.png 848w, https://substackcdn.com/image/fetch/$s_!MN76!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406d0a9f-3f59-4c9a-adc7-53bccf476d08_3536x188.png 1272w, https://substackcdn.com/image/fetch/$s_!MN76!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406d0a9f-3f59-4c9a-adc7-53bccf476d08_3536x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MN76!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406d0a9f-3f59-4c9a-adc7-53bccf476d08_3536x188.png" width="1456" height="77" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/406d0a9f-3f59-4c9a-adc7-53bccf476d08_3536x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:77,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:46764,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406d0a9f-3f59-4c9a-adc7-53bccf476d08_3536x188.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_!MN76!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406d0a9f-3f59-4c9a-adc7-53bccf476d08_3536x188.png 424w, https://substackcdn.com/image/fetch/$s_!MN76!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406d0a9f-3f59-4c9a-adc7-53bccf476d08_3536x188.png 848w, https://substackcdn.com/image/fetch/$s_!MN76!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406d0a9f-3f59-4c9a-adc7-53bccf476d08_3536x188.png 1272w, https://substackcdn.com/image/fetch/$s_!MN76!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406d0a9f-3f59-4c9a-adc7-53bccf476d08_3536x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>A highway does not improve because more cars enter it. It improves when more people reach their destination safely and predictably.</p><p>Software works the same way. Starting more work is not productivity. Finishing more valuable work with less waste is closer. Learning sooner is closer. Reducing the cost of change is closer. Creating customer behavior that justifies the investment is closer.</p><p>A development system can be full of motion and still void of progress.</p><h2>7. Systems thinking: the structure produces the behavior</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YSRk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf0bf644-bd8d-45c8-8ee0-cad027a1ab3f_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YSRk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf0bf644-bd8d-45c8-8ee0-cad027a1ab3f_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!YSRk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf0bf644-bd8d-45c8-8ee0-cad027a1ab3f_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!YSRk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf0bf644-bd8d-45c8-8ee0-cad027a1ab3f_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!YSRk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf0bf644-bd8d-45c8-8ee0-cad027a1ab3f_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YSRk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf0bf644-bd8d-45c8-8ee0-cad027a1ab3f_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/df0bf644-bd8d-45c8-8ee0-cad027a1ab3f_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:801114,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf0bf644-bd8d-45c8-8ee0-cad027a1ab3f_1024x558.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_!YSRk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf0bf644-bd8d-45c8-8ee0-cad027a1ab3f_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!YSRk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf0bf644-bd8d-45c8-8ee0-cad027a1ab3f_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!YSRk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf0bf644-bd8d-45c8-8ee0-cad027a1ab3f_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!YSRk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf0bf644-bd8d-45c8-8ee0-cad027a1ab3f_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Systems thinking helps us avoid the illusion that a single tool can transform an organization by itself.</p><p>Donella Meadows explains systems through elements, interrelations, and purpose. The visible parts matter, but behavior comes from how those parts are connected and what the system is organized to produce. Her Slinky example is useful because it shows that the same external action can produce different behavior depending on the internal structure of the system.</p><p>That is exactly what happens when <em>LLMs</em> enter software organizations.</p><p>AI does not enter an empty room. It enters incentives, queues, review habits, release mechanisms, product pressure, quality thresholds, budget models, team topology, architecture, customer feedback loops, and leadership beliefs. If the organization rewards output over outcomes, AI will not fix that. It will help the organization produce more of what it already rewards.</p><p>If teams are praised for shipping more features, AI will help ship more features. If teams are praised for closing tickets, AI will help close tickets. If teams are praised for visible busyness, AI will create more visible busyness. If nobody is accountable for adoption, deletion, cost of carry, or <em>ROI</em>, AI will not magically create that accountability.</p><p>This is why the problem is not only technical. It is structural.</p><p>A company may say its purpose is customer value, but if its budget process, planning rituals, promotion criteria, roadmap reviews, and executive dashboards reward output, then output is the real operating goal. Meadows&#8217; systems lens is uncomfortable because it asks us to infer purpose from behavior, not from slogans.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1IKo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F738414b6-a73e-48d9-a274-e409ab526843_4867x258.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1IKo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F738414b6-a73e-48d9-a274-e409ab526843_4867x258.png 424w, https://substackcdn.com/image/fetch/$s_!1IKo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F738414b6-a73e-48d9-a274-e409ab526843_4867x258.png 848w, https://substackcdn.com/image/fetch/$s_!1IKo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F738414b6-a73e-48d9-a274-e409ab526843_4867x258.png 1272w, https://substackcdn.com/image/fetch/$s_!1IKo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F738414b6-a73e-48d9-a274-e409ab526843_4867x258.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1IKo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F738414b6-a73e-48d9-a274-e409ab526843_4867x258.png" width="1456" height="77" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/738414b6-a73e-48d9-a274-e409ab526843_4867x258.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:77,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:74557,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F738414b6-a73e-48d9-a274-e409ab526843_4867x258.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_!1IKo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F738414b6-a73e-48d9-a274-e409ab526843_4867x258.png 424w, https://substackcdn.com/image/fetch/$s_!1IKo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F738414b6-a73e-48d9-a274-e409ab526843_4867x258.png 848w, https://substackcdn.com/image/fetch/$s_!1IKo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F738414b6-a73e-48d9-a274-e409ab526843_4867x258.png 1272w, https://substackcdn.com/image/fetch/$s_!1IKo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F738414b6-a73e-48d9-a274-e409ab526843_4867x258.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>This loop is not a prediction that every organization will suffer in the same way. It is a warning about a plausible structure. When the system rewards output, more generated code can increase review pressure. More review pressure can create queues. Queues can create pressure to move faster. Pressure can produce shallower review. Shallow review can increase defects and rework. Rework consumes capacity, and lower capacity creates more pressure to produce.</p><p>That is why the AI productivity debate should not start with &#8220;Which tool should we buy?&#8221; It should start with &#8220;What does our system currently reward?&#8221;</p><p>If the answer is output, AI may make the system worse while making the dashboard look better.</p><h2>8. What the evidence actually says</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5b0L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23375bc8-711a-4fc7-880d-ea5353b7bb5c_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5b0L!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23375bc8-711a-4fc7-880d-ea5353b7bb5c_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!5b0L!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23375bc8-711a-4fc7-880d-ea5353b7bb5c_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!5b0L!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23375bc8-711a-4fc7-880d-ea5353b7bb5c_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!5b0L!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23375bc8-711a-4fc7-880d-ea5353b7bb5c_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5b0L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23375bc8-711a-4fc7-880d-ea5353b7bb5c_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/23375bc8-711a-4fc7-880d-ea5353b7bb5c_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:820550,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23375bc8-711a-4fc7-880d-ea5353b7bb5c_1024x558.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_!5b0L!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23375bc8-711a-4fc7-880d-ea5353b7bb5c_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!5b0L!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23375bc8-711a-4fc7-880d-ea5353b7bb5c_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!5b0L!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23375bc8-711a-4fc7-880d-ea5353b7bb5c_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!5b0L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23375bc8-711a-4fc7-880d-ea5353b7bb5c_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The evidence around AI-assisted software development is still developing, and it does not support a simple slogan. It does not support &#8220;AI makes organizations productive.&#8221; It also does not support &#8220;AI is useless.&#8221; The more careful reading is this: local coding activity can increase, but the effect weakens as we move toward production, usage, quality, and economic return.</p><p>The <em>SPACE</em> framework matters because it was created to avoid the mistake of reducing developer productivity to one activity metric. It defines developer productivity across satisfaction and well-being, performance, activity, communication and collaboration, and efficiency and flow. It also names &#8220;productivity is all about developer activity&#8221; as a myth and warns that activity metrics should not be used in isolation. (<a href="https://queue.acm.org/detail.cfm?id=3454124">queue.acm.org</a>)</p><p>DORA is useful because it studies software delivery performance and organizational capabilities. Its 2025 AI-assisted software development report does not say that buying AI tools is enough. It says AI acts as an amplifier, magnifying an organization&#8217;s existing strengths and weaknesses, and that the greatest returns come from focusing on the underlying organizational system rather than the tools alone. (<a href="https://dora.dev/dora-report-2025/">dora.dev</a>)</p><p>Faros gives us a sharper operational warning because its 2026 report analyzes engineering telemetry from 22,000 developers and 4,000 teams. That matters because telemetry lets us look beyond how productive people feel and inspect downstream signals such as review time, code quality, incidents, churn, and lead time. Faros reports increased task completion and code-related work, but also reports that bugs per developer rose 54 percent, the incidents-to-pull-request ratio more than tripled, median review time increased 5x, and 31 percent more pull requests merged without review.</p><p>The throughput section of the Faros report is especially important. It reports task throughput per developer up 33.7 percent, epics completed per developer up 66.2 percent, pull-request merge rate up 16.2 percent, and code-related tasks up 210 percent. But deployment frequency is the exception: deployments per week fell in the measured subset, while code churn increased 861 percent. Faros itself warns that throughput measures what was shipped, not what survived.</p><p>The NBER working paper <em>Writing Code vs. Shipping Code</em> studies productivity effects across generations of AI coding tools. The public NBER page identifies it as a 2026 working paper by Mert Demirer, Leon Musolff, and Liyuan Yang, and the public PDF preview says the effect builds gradually, consistent with the lag between writing code and shipping a release. (<a href="https://www.nber.org/papers/w35275">NBER</a>)</p><p>That evidence is valuable because it separates writing code from shipping code. It also suggests that even release-like output is not the end of the story. The chart that motivated this article makes the next step visible: more app releases do not automatically imply more significant usage, more reviews, or more traction.</p><p>Pendo&#8217;s 2019 feature adoption report adds the product-side warning. It analyzed anonymized product usage data and found that 80 percent of features in the average software product were rarely or never used. The PDF also reports that 12 percent of features generated 80 percent of average daily usage volume. This is vendor research, so it should not be treated as a universal law. Still, it is a useful warning: many organizations already build more functionality than users meaningfully adopt. (<a href="https://www.pendo.io/resources/the-2019-feature-adoption-report/">Pendo.io</a>)</p><p>Token pricing makes the cost side more visible. GitHub announced that Copilot plans transition to usage-based billing on June 1, 2026, with usage calculated from token consumption, including input, output, and cached tokens. GitHub&#8217;s documentation also explains that Copilot interactions consume input tokens, output tokens, and cached tokens, with the total converted into AI credits. OpenAI&#8217;s pricing documentation says tokens are billed at the chosen model&#8217;s input and output rates, and Anthropic&#8217;s pricing documentation describes cache write and cache read token pricing. (<a href="https://github.blog/news-insights/company-news/github-copilot-is-moving-to-usage-based-billing/">The GitHub Blog</a>)</p><p>The responsible conclusion is not that AI reduces productivity. That would be too blunt.</p><p>The responsible conclusion is sharper: some datasets show local acceleration in coding activity and upstream delivery metrics, but organizational productivity remains unproven unless the organization can show better outcomes, better flow, better quality, lower risk, lower cost of ownership, or higher <em>ROI</em>.</p><h2>9. The AI-assisted feature equation</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4C9r!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c3f24a5-cc65-445e-97b4-23899f68b6dd_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4C9r!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c3f24a5-cc65-445e-97b4-23899f68b6dd_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!4C9r!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c3f24a5-cc65-445e-97b4-23899f68b6dd_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!4C9r!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c3f24a5-cc65-445e-97b4-23899f68b6dd_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!4C9r!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c3f24a5-cc65-445e-97b4-23899f68b6dd_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4C9r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c3f24a5-cc65-445e-97b4-23899f68b6dd_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2c3f24a5-cc65-445e-97b4-23899f68b6dd_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:632854,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c3f24a5-cc65-445e-97b4-23899f68b6dd_1024x559.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_!4C9r!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c3f24a5-cc65-445e-97b4-23899f68b6dd_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!4C9r!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c3f24a5-cc65-445e-97b4-23899f68b6dd_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!4C9r!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c3f24a5-cc65-445e-97b4-23899f68b6dd_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!4C9r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c3f24a5-cc65-445e-97b4-23899f68b6dd_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A feature is an investment equation before it is a product announcement.</p><p>A useful thinking model is not &#8220;feature cost equals build cost.&#8221; Build cost is only one line in the equation. The economic result of a feature depends on the benefit it may produce, the cost of creating it, the cost of carrying it, and the risks attached to being wrong.</p><p>A better model is this:</p><pre><code><code>Expected economic result of a feature =
expected benefit
- opportunity cost
- discovery cost
- build cost
- review cost
- testing cost
- deployment cost
- operational cost
- maintenance cost
- basal cost
- cost of carry
- product adoption risk
- technical risk
- operational risk
- rework risk</code></code></pre><p>The product adoption risk deserves to be explicit. It is the risk that users do not want, adopt, understand, or value the feature. That risk is not secondary. In product software, it is often the central risk. A feature is not valuable because it exists. It becomes valuable when users, customers, or the organization absorb it in a way that creates return.</p><p>With <em>LLM</em>-based AI, the equation does not disappear. It changes shape:</p><pre><code><code>Expected economic result of an AI-assisted feature =
expected benefit
- opportunity cost
- discovery cost
- prompting and orchestration cost
- token cost
- build cost
- review cost
- testing cost
- deployment cost
- operational cost
- maintenance cost
- basal cost
- cost of carry
- product adoption risk
- technical risk
- operational risk
- rework risk</code></code></pre><p>Some costs may go down. It is plausible that <em>LLMs</em> reduce the cost of scaffolding, exploration, code generation, migration drafts, repetitive changes, test data generation, documentation drafts, and some forms of technical discovery. That should be measured, not assumed.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NTmN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabf36fcb-caa9-4eec-96cc-6859a8c352d4_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NTmN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabf36fcb-caa9-4eec-96cc-6859a8c352d4_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!NTmN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabf36fcb-caa9-4eec-96cc-6859a8c352d4_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!NTmN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabf36fcb-caa9-4eec-96cc-6859a8c352d4_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!NTmN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabf36fcb-caa9-4eec-96cc-6859a8c352d4_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NTmN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabf36fcb-caa9-4eec-96cc-6859a8c352d4_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/abf36fcb-caa9-4eec-96cc-6859a8c352d4_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:844008,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabf36fcb-caa9-4eec-96cc-6859a8c352d4_1024x558.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_!NTmN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabf36fcb-caa9-4eec-96cc-6859a8c352d4_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!NTmN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabf36fcb-caa9-4eec-96cc-6859a8c352d4_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!NTmN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabf36fcb-caa9-4eec-96cc-6859a8c352d4_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!NTmN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabf36fcb-caa9-4eec-96cc-6859a8c352d4_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Some costs may go up. Prompting and orchestration can become real work. Token usage can become a visible operating cost. Review may become harder if generated changes are larger. Testing may need to become stronger if confidence falls. Rework may increase if accepted code is not production-ready. Maintenance may increase if the organization ships more surface area. Basal cost increases when more features remain alive without creating enough value.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6sgh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d8a25a-3528-43ce-af38-13dc9a18031c_6148x812.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6sgh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d8a25a-3528-43ce-af38-13dc9a18031c_6148x812.png 424w, https://substackcdn.com/image/fetch/$s_!6sgh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d8a25a-3528-43ce-af38-13dc9a18031c_6148x812.png 848w, https://substackcdn.com/image/fetch/$s_!6sgh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d8a25a-3528-43ce-af38-13dc9a18031c_6148x812.png 1272w, https://substackcdn.com/image/fetch/$s_!6sgh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d8a25a-3528-43ce-af38-13dc9a18031c_6148x812.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6sgh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d8a25a-3528-43ce-af38-13dc9a18031c_6148x812.png" width="1456" height="192" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/35d8a25a-3528-43ce-af38-13dc9a18031c_6148x812.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:192,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:215163,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d8a25a-3528-43ce-af38-13dc9a18031c_6148x812.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_!6sgh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d8a25a-3528-43ce-af38-13dc9a18031c_6148x812.png 424w, https://substackcdn.com/image/fetch/$s_!6sgh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d8a25a-3528-43ce-af38-13dc9a18031c_6148x812.png 848w, https://substackcdn.com/image/fetch/$s_!6sgh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d8a25a-3528-43ce-af38-13dc9a18031c_6148x812.png 1272w, https://substackcdn.com/image/fetch/$s_!6sgh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d8a25a-3528-43ce-af38-13dc9a18031c_6148x812.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The economic question is not whether AI made one line cheaper. The economic question is whether the total system became cheaper, safer, faster to learn from, and more capable of producing return.</p><p>If the savings in build cost are eaten by higher review cost, higher rework, higher token spend, higher maintenance, higher basal cost, and the same or worse adoption risk, the organization has not improved the outcome. It has only moved cost from one column to another.</p><p>The dangerous move is to look only at reduced build cost and declare victory.</p><p>That is like saying a mortgage is cheap because the first payment was small.</p><h2>10. What organizations should measure instead</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uQte!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda61b536-dc4a-4bf0-b5e0-46ecf558c4d8_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uQte!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda61b536-dc4a-4bf0-b5e0-46ecf558c4d8_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uQte!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda61b536-dc4a-4bf0-b5e0-46ecf558c4d8_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uQte!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda61b536-dc4a-4bf0-b5e0-46ecf558c4d8_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uQte!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda61b536-dc4a-4bf0-b5e0-46ecf558c4d8_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uQte!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda61b536-dc4a-4bf0-b5e0-46ecf558c4d8_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/da61b536-dc4a-4bf0-b5e0-46ecf558c4d8_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:767082,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda61b536-dc4a-4bf0-b5e0-46ecf558c4d8_1024x559.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_!uQte!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda61b536-dc4a-4bf0-b5e0-46ecf558c4d8_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uQte!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda61b536-dc4a-4bf0-b5e0-46ecf558c4d8_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uQte!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda61b536-dc4a-4bf0-b5e0-46ecf558c4d8_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uQte!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda61b536-dc4a-4bf0-b5e0-46ecf558c4d8_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A productive organization does not merely produce more. It converts more of its investment into useful outcomes with less waste, lower risk, and better optionality.</p><p>That requires a different measurement conversation.</p><p>First, measure adoption. Are users adopting the shipped changes? Are customers reaching value sooner? Are feature usage, retention, conversion, revenue, or operational efficiency improving? Are we measuring actual behavior, or only internal activity?</p><p>Second, measure flow across the whole value stream. Is valuable work moving faster from idea to validated outcome? Is lead time improving from concept to production, not only from coding start to pull request? Are review queues, QA queues, release queues, and decision queues shrinking?</p><p>Third, measure investment quality. Are bets smaller? Is opportunity cost lower? Are we learning earlier? Are we avoiding large batches of speculative functionality? Are we killing weak ideas before they become expensive software?</p><p>Fourth, measure quality and safety. Are bugs, incidents, rework, rollback frequency, change failure rate, and support load improving? Or are we raising output while lowering the quality threshold?</p><p>Fifth, measure maintenance burden. Is the codebase becoming easier to understand, change, test, and operate? Are we reducing basal cost, or adding permanent complexity?</p><p>Sixth, measure deletion discipline. Are unused features removed? Do we know which features are rarely used? Can we safely delete code, flags, screens, options, endpoints, reports, and workflows that no longer justify their existence?</p><p>Seventh, measure liquidity. Can the organization redirect effort quickly when evidence changes? Can it pause work, split work, abandon weak bets, and reinvest in stronger ones?</p><p>Eighth, measure human system health. Are developers, reviewers, product people, support teams, and operators less overloaded? Or has AI shifted pressure from writing code to reviewing, coordinating, cleaning, and firefighting?</p><p>These questions are less glamorous than &#8220;How much faster can we code?&#8221; They are also more economically honest.</p><p>A company can claim AI improved organizational productivity only when it can show that more value flows through the whole system, not merely that more output exits the development subsystem.</p><h2>11. Conclusions</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9J2R!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6157a6b-c6c8-488c-80ed-95e3f316e6d2_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9J2R!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6157a6b-c6c8-488c-80ed-95e3f316e6d2_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!9J2R!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6157a6b-c6c8-488c-80ed-95e3f316e6d2_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!9J2R!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6157a6b-c6c8-488c-80ed-95e3f316e6d2_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!9J2R!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6157a6b-c6c8-488c-80ed-95e3f316e6d2_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9J2R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6157a6b-c6c8-488c-80ed-95e3f316e6d2_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c6157a6b-c6c8-488c-80ed-95e3f316e6d2_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:671827,&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://emmanuelvalverderamos.substack.com/i/201108250?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6157a6b-c6c8-488c-80ed-95e3f316e6d2_1024x559.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_!9J2R!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6157a6b-c6c8-488c-80ed-95e3f316e6d2_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!9J2R!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6157a6b-c6c8-488c-80ed-95e3f316e6d2_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!9J2R!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6157a6b-c6c8-488c-80ed-95e3f316e6d2_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!9J2R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6157a6b-c6c8-488c-80ed-95e3f316e6d2_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>LLM</em>-based AI changes software development, but it does not repeal software economics.</p><p>It can help developers explore faster, draft faster, generate faster, automate repetitive changes, summarize unfamiliar code, and create useful leverage in the right context. None of that should be dismissed. The argument is not that AI is useless. The argument is that &#8220;more code&#8221; is not the same as &#8220;more organizational productivity.&#8221;</p><p>Productivity is not a property of the development team alone. It is a property of the whole system that turns investment into outcomes. If one subsystem accelerates while the rest of the system becomes overloaded, the organization has not necessarily improved. It may have created a faster path to waste.</p><p>A company that ships 100 features nobody wants is not more productive than a company that ships 10 features customers actually use. It is not more innovative because its backlog burns faster. It is not more efficient because its developers accept more generated code. It is not more competitive because its product surface area expands.</p><p>The serious productivity question is harder. Did we create more useful value? Did we learn sooner? Did we reduce the cost of change? Did we improve flow across the whole value stream? Did we reduce risk? Did we remove what did not work? Did the investment return more than it consumed?</p><p>If the answer is no, then AI did not make the organization more productive. It made one subsystem louder.</p><p>And this is where the conversation should stop being polite.</p><p><strong>Spending more money to generate more code and calling it productivity does not make you more productive. It makes you a worse steward of capital.</strong></p><p>If you can generate 30 percent more features but cannot show more adoption, more traction, more revenue, more learning, more cost reduction, or better <em>Return on Investment</em> (<em>ROI</em>), those features are not proof of productivity. They are a larger cost base.</p><p>You are not celebrating productivity. You are celebrating the ability to spend more while producing less economic value.</p><p>If you can create more features than before and still fail to increase <em>ROI</em>, the conclusion is not that the software industry has changed beyond recognition. The conclusion is simpler and harsher: you never understood the economics of software in the first place.</p><p>That says more about you as a business operator, and more about your organization, than it says about the future of software.</p><p>The future advantage will not belong to organizations that generate the most software. It will belong to organizations that can decide what should exist, validate it quickly, ship it safely, measure whether it matters, and delete it when it does not.</p><p>In an age where producing software becomes easier, judgment becomes the scarce resource.</p><h2>12. References</h2><ul><li><p>Anthropic. <em>Claude API Pricing</em>. <a href="https://platform.claude.com/docs/en/about-claude/pricing">https://platform.claude.com/docs/en/about-claude/pricing</a></p></li><li><p>Anthropic. <em>Prompt Caching</em>. <a href="https://platform.claude.com/docs/en/build-with-claude/prompt-caching">https://platform.claude.com/docs/en/build-with-claude/prompt-caching</a></p></li><li><p>Demirer, Mert, Musolff, Leon, Yang, Liyuan. <em>Writing Code vs. Shipping Code: Productivity Effects Across Generations of AI Coding Tools</em>. <a href="https://www.nber.org/papers/w35275">https://www.nber.org/papers/w35275</a></p></li><li><p>DORA. <em>State of AI-assisted Software Development 2025</em>. <a href="https://dora.dev/dora-report-2025/">https://dora.dev/dora-report-2025/</a></p></li><li><p>Faros AI. <em>AI Engineering Report 2026: The Acceleration Whiplash</em>. <a href="https://www.faros.ai/blog/ai-acceleration-whiplash-takeaways">https://www.faros.ai/blog/ai-acceleration-whiplash-takeaways</a></p></li><li><p>Ferro Aldama, Eduardo. <em>Basal Cost of Software</em>. <a href="https://www.eferro.net/2021/02/basal-cost-of-software.html">https://www.eferro.net/2021/02/basal-cost-of-software.html</a></p></li><li><p>Forsgren, Nicole, Storey, Margaret-Anne, Maddila, Chandra, Zimmermann, Thomas, Houck, Brian, Butler, Jenna. <em>The SPACE of Developer Productivity</em>. <a href="https://queue.acm.org/detail.cfm?id=3454124">https://queue.acm.org/detail.cfm?id=3454124</a></p></li><li><p>GitHub. <em>GitHub Copilot Is Moving to Usage-based Billing</em>. <a href="https://github.blog/news-insights/company-news/github-copilot-is-moving-to-usage-based-billing/">https://github.blog/news-insights/company-news/github-copilot-is-moving-to-usage-based-billing/</a></p></li><li><p>GitHub Docs. <em>Usage-based Billing for Organizations and Enterprises</em>. <a href="https://docs.github.com/en/copilot/concepts/billing/usage-based-billing-for-organizations-and-enterprises">https://docs.github.com/en/copilot/concepts/billing/usage-based-billing-for-organizations-and-enterprises</a></p></li><li><p>Meadows, Donella H. <em>Thinking in Systems: A Primer</em>. <a href="https://www.chelseagreen.com/product/thinking-in-systems/">https://www.chelseagreen.com/product/thinking-in-systems/</a></p></li><li><p>OpenAI. <em>API Pricing</em>. <a href="https://developers.openai.com/api/docs/pricing">https://developers.openai.com/api/docs/pricing</a></p></li><li><p>Pendo. <em>The 2019 Feature Adoption Report</em>. <a href="https://www.pendo.io/resources/the-2019-feature-adoption-report/">https://www.pendo.io/resources/the-2019-feature-adoption-report/</a></p></li><li><p>Poppendieck, Mary, Poppendieck, Tom. <em>Implementing Lean Software Development: From Concept to Cash</em>. <a href="https://www.pearson.com/en-us/subject-catalog/p/implementing-lean-software-development-from-concept-to-cash/P200000003154">https://www.pearson.com/en-us/subject-catalog/p/implementing-lean-software-development-from-concept-to-cash/P200000003154</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Parallel Change: how to evolve live systems without breaking consumers]]></title><description><![CDATA[The fear is rational]]></description><link>https://emmanuelvalverderamos.substack.com/p/parallel-change-how-to-evolve-live</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/parallel-change-how-to-evolve-live</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 09 Jun 2026 05:00:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!8G2E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F067357c3-2941-48ee-a4d6-db1661701644_1860x1676.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>The fear is rational</h2><p>A <code>users</code> table has a <code>full_name</code> column. Product wants <code>first_name</code> and <code>last_name</code>. The table has two million rows. Writes are happening every second. Several parts of the system read the column. A mobile app depends on the API, and your team does not control when that app reaches its users. There is no maintenance window, no quiet deployment slot, and no moment where every consumer will stop long enough for you to change the shape in peace.</p><p>In isolation, the technical change looks small. In production, it does not. The backlog item stays open not because nobody understands the benefit, but because the path to the benefit looks risky. Separate first and last name fields are easier to validate, sort, display, and reason about than a single overloaded string. But the system around that change makes the obvious implementation feel dangerous.</p><p>That fear is healthy. Engineers have seen systems break when too many consumers had to move together. They have seen changes that looked small cause incidents because the real problem was never the code in isolation. The real problem was the coordination burden hiding around it.</p><p>The fear is rational. The risk is real. What is irrational is the assumption underneath the fear: that the change has to happen all at once.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>The assumption and why it fails in real systems</h2><p>Most engineers first learn to make changes in a simple world: one codebase, one runtime, one schema, one team, one deployment. In that world, a breaking change often feels atomic. You update the code, run the tests, deploy once, and the system moves cleanly from the old shape to the new one.</p><p>That mental model breaks as soon as the system stops being truly atomic in production. Multiple consumers moving at different speeds are one reason. But the same failure appears when old and new binaries coexist during a rolling deployment, when persistent data outlives the code that writes it, when traffic moves gradually through a flag or canary, or when an infrastructure dependency must be replaced while releases continue. The interface may be a constructor signature, a table schema, an API response, a message payload, or a whole application boundary.</p><p>This is why trunk-based development changes the conversation. Trunk-based development is the practice of integrating all changes directly into the main branch of the repository continuously, without long-lived feature branches. The goal is that the build is always green and that any commit on trunk is deployable at any moment. If your team works this way, every step of a change must be a commit that breaks nothing, because any other engineer can deploy trunk at any time. That constraint is not a burden. It is a forcing function that makes the hidden assumption visible.</p><p>The dangerous assumption is not &#8220;this change has risk.&#8221; That part is true. The dangerous assumption is &#8220;every consumer has to move together.&#8221; In real systems there is rarely one clean instant where before ends and after begins. For a while, both are true. Some consumers are on the last deployment, some are on the current one, some data was written before the migration logic existed, and some will be written after the migration has completed.</p><p>The system is not switching states. It is moving through states. The right question is not &#8220;when can we do this all at once?&#8221; The right question is &#8220;what sequence of safe intermediate states gets us from here to there?&#8221;</p><div><hr></div><h2>What Parallel Change is</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PI36!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6d52ac-100d-42bb-8499-d237a488f9fc_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PI36!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6d52ac-100d-42bb-8499-d237a488f9fc_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!PI36!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6d52ac-100d-42bb-8499-d237a488f9fc_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!PI36!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6d52ac-100d-42bb-8499-d237a488f9fc_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!PI36!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6d52ac-100d-42bb-8499-d237a488f9fc_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PI36!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6d52ac-100d-42bb-8499-d237a488f9fc_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cd6d52ac-100d-42bb-8499-d237a488f9fc_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1704632,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6d52ac-100d-42bb-8499-d237a488f9fc_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PI36!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6d52ac-100d-42bb-8499-d237a488f9fc_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!PI36!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6d52ac-100d-42bb-8499-d237a488f9fc_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!PI36!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6d52ac-100d-42bb-8499-d237a488f9fc_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!PI36!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6d52ac-100d-42bb-8499-d237a488f9fc_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Parallel Change is a way to evolve a contract without forcing every dependent part of the system to adopt the new shape immediately. It is also known as Expand and Contract, a term that comes from the world of database evolution. &#8220;Expand&#8221; refers to the act of adding without removing. &#8220;Contract&#8221; refers to the act of removing once nothing depends on what was added. The name &#8220;Parallel Change&#8221; emphasises that the two states coexist during the migrate phase: the system runs with the old and new interfaces both active at the same time.</p><p>The pattern breaks any backward-incompatible change into three phases: expand, migrate, and contract. Those phases reduce to two rules.</p><p>Never remove something until nothing depends on it. Never force adoption of something that does not yet exist.</p><p>Before going further, one clarification about the word &#8220;interface.&#8221; It does not mean only a method signature or a C# interface declaration. It means any surface through which two parts of a system communicate and depend on each other&#8217;s shape. A constructor parameter list is an interface. A database table is an interface. A REST endpoint is an interface. A message queue schema is an interface. Even a deployed application version can behave like an interface when two versions coexist temporarily during a rollout.</p><p>What makes these interfaces hard to evolve is not complexity in isolation. It is that the system must pass through a period where old and new shapes both exist. That period can be forced by many things: live writes that continue through a deployment, old and new binaries running side by side during a rolling update, persistent rows that were written before the migration logic existed, teams or services that deploy independently, traffic moving gradually through a feature flag, a dependency being replaced while releases continue, or a legacy module being displaced by a new one. Multiple consumers moving at different speeds are one reason. They are one item in a longer list.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SJjC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b90b7c3-f0dc-45a2-8b8d-acc1c65b6150_1272x1912.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SJjC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b90b7c3-f0dc-45a2-8b8d-acc1c65b6150_1272x1912.png 424w, https://substackcdn.com/image/fetch/$s_!SJjC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b90b7c3-f0dc-45a2-8b8d-acc1c65b6150_1272x1912.png 848w, https://substackcdn.com/image/fetch/$s_!SJjC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b90b7c3-f0dc-45a2-8b8d-acc1c65b6150_1272x1912.png 1272w, https://substackcdn.com/image/fetch/$s_!SJjC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b90b7c3-f0dc-45a2-8b8d-acc1c65b6150_1272x1912.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SJjC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b90b7c3-f0dc-45a2-8b8d-acc1c65b6150_1272x1912.png" width="1272" height="1912" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9b90b7c3-f0dc-45a2-8b8d-acc1c65b6150_1272x1912.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1912,&quot;width&quot;:1272,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:195571,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b90b7c3-f0dc-45a2-8b8d-acc1c65b6150_1272x1912.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_!SJjC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b90b7c3-f0dc-45a2-8b8d-acc1c65b6150_1272x1912.png 424w, https://substackcdn.com/image/fetch/$s_!SJjC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b90b7c3-f0dc-45a2-8b8d-acc1c65b6150_1272x1912.png 848w, https://substackcdn.com/image/fetch/$s_!SJjC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b90b7c3-f0dc-45a2-8b8d-acc1c65b6150_1272x1912.png 1272w, https://substackcdn.com/image/fetch/$s_!SJjC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b90b7c3-f0dc-45a2-8b8d-acc1c65b6150_1272x1912.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Once you stop seeing Parallel Change as &#8220;that database migration technique&#8221; or &#8220;that thing you do when multiple consumers exist&#8221; and start seeing it as a general discipline for safe evolution under mixed states, it becomes available in many places where teams usually attempt a risky atomic cut.</p><h2>The mindset shift</h2><p>The hardest part of Parallel Change is not memorising the three phases. The hardest part is replacing a reflex.</p><p>The reflex is to begin with the target design and ask: &#8220;what do I need to change?&#8221; In a live system that question is incomplete. It hides the operational problem inside an unspoken &#8220;then we switch everything over.&#8221; Parallel Change replaces it with a better question: &#8220;what sequence of safe intermediate states gets me from the current system to the target system?&#8221;</p><p>Under the old framing, the change often becomes one large coordinated effort. Everything touching the migration is grouped into a single delivery: domain logic, persistence, migrations, API contracts, and frontend usage all at once. Reviewers have to hold the entire transformation in their heads. Deployment is fragile because several moving parts must align. Rollback is painful because reverting one part may not revert the others safely.</p><p>Under the new framing, the first good change is boring on purpose. It only adds. It cannot break anything. It can be deployed, observed, and rolled back completely independently of every subsequent step. That narrowness is not a constraint. It is the point.</p><p>There is a second shift that matters just as much, and it is less obvious. Sometimes Parallel Change is not only a way to protect the system while making a change you already understand. The safe, gradual nature of the migration gives the team time and space to arrive at a better understanding of what the domain actually looks like. An inheritance hierarchy that felt necessary before the migration might reveal itself as a conflation of two separate concepts once you slow down enough to see what would simplify if you split them. The migration is not just the mechanism. Sometimes it is the lens.</p><p>The corollary that engineers most often miss: the contract step is not optional. If you expand and migrate but never contract, you have not simplified the system. You have added complexity to it. The old interface still exists. New engineers do not know which path is current. Future changes need to account for both shapes. Schedule the contract step when you open the expand change. They are one decision made at one moment, not two separate decisions made months apart.</p><h2>The three phases in detail</h2><p>Before the examples, it is worth being precise about what each phase is for, what it allows, and where the real operational danger appears.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Eo0Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bab3f71-59db-439f-9515-5b7bfe7cc8d3_3180x2329.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Eo0Z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bab3f71-59db-439f-9515-5b7bfe7cc8d3_3180x2329.png 424w, https://substackcdn.com/image/fetch/$s_!Eo0Z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bab3f71-59db-439f-9515-5b7bfe7cc8d3_3180x2329.png 848w, https://substackcdn.com/image/fetch/$s_!Eo0Z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bab3f71-59db-439f-9515-5b7bfe7cc8d3_3180x2329.png 1272w, https://substackcdn.com/image/fetch/$s_!Eo0Z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bab3f71-59db-439f-9515-5b7bfe7cc8d3_3180x2329.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Eo0Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bab3f71-59db-439f-9515-5b7bfe7cc8d3_3180x2329.png" width="1456" height="1066" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1bab3f71-59db-439f-9515-5b7bfe7cc8d3_3180x2329.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1066,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:738077,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bab3f71-59db-439f-9515-5b7bfe7cc8d3_3180x2329.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_!Eo0Z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bab3f71-59db-439f-9515-5b7bfe7cc8d3_3180x2329.png 424w, https://substackcdn.com/image/fetch/$s_!Eo0Z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bab3f71-59db-439f-9515-5b7bfe7cc8d3_3180x2329.png 848w, https://substackcdn.com/image/fetch/$s_!Eo0Z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bab3f71-59db-439f-9515-5b7bfe7cc8d3_3180x2329.png 1272w, https://substackcdn.com/image/fetch/$s_!Eo0Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bab3f71-59db-439f-9515-5b7bfe7cc8d3_3180x2329.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Expand</h3><p>Expand is the safest phase because it only adds. At the end of Expand, the new shape exists but the old shape still works. Old consumers continue untouched. New consumers have somewhere safe to move. The system is deliberately more flexible than usual.</p><p>In code, Expand might mean adding a new constructor while keeping the old one delegating to it. In a database, it means adding new nullable columns while leaving the old ones intact. In an API, it means introducing <code>v2</code> while keeping <code>v1</code> alive. In a deployment, it means introducing a new implementation behind an abstraction or a flag while the existing implementation still serves traffic.</p><p>The system can be deployed at any point during Expand and continues to function correctly.</p><h3>Migrate</h3><p>Migrate is the phase where the real movement happens. Consumers move one by one. Existing rows get backfilled. Reads switch from old fields to new fields. Frontends move to new endpoints. Services start publishing or consuming the new message shape.</p><p>This is the phase with the most variety in technique. Rather than treating all migrations the same, it helps to have a shared vocabulary for the specific mechanism in use.</p><p><strong>Delegation</strong> is used when an old code-level interface needs to stay valid while a new one takes over. The old constructor or method calls the new one internally. No caller breaks. The <code>[Obsolete]</code> attribute warns without preventing compilation. This is the technique in Example 1.</p><p><strong>Temporary scaffolding</strong> is used for internal refactorings, particularly in test code, where many call sites use an old interface and need time to migrate. Transitional overloads, conditional adapters, or delegating builders are introduced solely to let old and new internal interfaces coexist. The scaffolding is explicitly temporary and deleted in the contract phase. This is the technique in Example 1.5.</p><p><strong>Dual-write</strong> is used when a persistent store must carry two representations of the same data during migration. Every write operation populates both the old and new fields simultaneously, keeping both read paths current regardless of which is active. This appears in Examples 2 and 4.</p><p><strong>Batched backfill</strong> is used to populate existing rows with values for a new column or field structure. On a table with millions of rows, a single-transaction mass update can lock the table for minutes. Running it in batches with a short pause between each leaves room for normal production writes and avoids exhausting the database server. This appears in Examples 2 and 4.</p><p><strong>Feature flag with runtime switch</strong> is used when a behaviour needs to change at runtime without a deployment. The flag controls which read path, endpoint version, or render logic is active. Changing the flag in the dashboard takes effect within the poll interval. This appears in Examples 2, 3, and 5.</p><p><strong>API versioning</strong> is used when using an application contract changes in a way that breaks existing consumers. A new versioned endpoint carries the new contract. The old endpoint stays alive until all consumers have migrated. This appears in Example 3.</p><p><strong>Compatibility view</strong> is used when a database table or column names need to be renamed. A SQL view exposes the new names over the old structure at zero cost. New code reads from the view. Old code reads from the table. Both hit the same underlying data. This appears in Example 4.</p><p>Two additional techniques operate at a scale above the constructor and database examples but follow the same three-phase discipline. Branch by Abstraction is a technique for replacing an infrastructure dependency while the system continues running: a stable interface is introduced between callers and the current implementation, the new implementation is built behind it, and a feature flag routes traffic between old and new until the old one can be deleted. This is the technique in Example 6. Strangler Fig is the system-level version: new functionality is built as a separate deployable alongside a legacy module, a routing layer directs traffic incrementally from old to new, and the legacy module is deleted when it has no remaining consumers. This is the technique in Example 7. The distinction between them is the location of the suture: inside the code in Branch by Abstraction, in the routing layer in Strangler Fig.</p><p>The following diagram maps every technique in this article to the type of seam it addresses:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_oUS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364ce5bd-68b5-4caf-a18c-597cf639e4b0_3594x2157.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_oUS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364ce5bd-68b5-4caf-a18c-597cf639e4b0_3594x2157.png 424w, https://substackcdn.com/image/fetch/$s_!_oUS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364ce5bd-68b5-4caf-a18c-597cf639e4b0_3594x2157.png 848w, https://substackcdn.com/image/fetch/$s_!_oUS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364ce5bd-68b5-4caf-a18c-597cf639e4b0_3594x2157.png 1272w, https://substackcdn.com/image/fetch/$s_!_oUS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364ce5bd-68b5-4caf-a18c-597cf639e4b0_3594x2157.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_oUS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364ce5bd-68b5-4caf-a18c-597cf639e4b0_3594x2157.png" width="1456" height="874" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/364ce5bd-68b5-4caf-a18c-597cf639e4b0_3594x2157.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:874,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1481774,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364ce5bd-68b5-4caf-a18c-597cf639e4b0_3594x2157.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_!_oUS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364ce5bd-68b5-4caf-a18c-597cf639e4b0_3594x2157.png 424w, https://substackcdn.com/image/fetch/$s_!_oUS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364ce5bd-68b5-4caf-a18c-597cf639e4b0_3594x2157.png 848w, https://substackcdn.com/image/fetch/$s_!_oUS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364ce5bd-68b5-4caf-a18c-597cf639e4b0_3594x2157.png 1272w, https://substackcdn.com/image/fetch/$s_!_oUS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F364ce5bd-68b5-4caf-a18c-597cf639e4b0_3594x2157.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ztO0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665ed1e3-38d5-4a16-9aef-57d96edb5be9_1860x540.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ztO0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665ed1e3-38d5-4a16-9aef-57d96edb5be9_1860x540.png 424w, https://substackcdn.com/image/fetch/$s_!ztO0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665ed1e3-38d5-4a16-9aef-57d96edb5be9_1860x540.png 848w, https://substackcdn.com/image/fetch/$s_!ztO0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665ed1e3-38d5-4a16-9aef-57d96edb5be9_1860x540.png 1272w, https://substackcdn.com/image/fetch/$s_!ztO0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665ed1e3-38d5-4a16-9aef-57d96edb5be9_1860x540.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ztO0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665ed1e3-38d5-4a16-9aef-57d96edb5be9_1860x540.png" width="1456" height="423" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/665ed1e3-38d5-4a16-9aef-57d96edb5be9_1860x540.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:423,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:178662,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665ed1e3-38d5-4a16-9aef-57d96edb5be9_1860x540.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_!ztO0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665ed1e3-38d5-4a16-9aef-57d96edb5be9_1860x540.png 424w, https://substackcdn.com/image/fetch/$s_!ztO0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665ed1e3-38d5-4a16-9aef-57d96edb5be9_1860x540.png 848w, https://substackcdn.com/image/fetch/$s_!ztO0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665ed1e3-38d5-4a16-9aef-57d96edb5be9_1860x540.png 1272w, https://substackcdn.com/image/fetch/$s_!ztO0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665ed1e3-38d5-4a16-9aef-57d96edb5be9_1860x540.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kESV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9411585d-8ac6-4001-9926-c110d4222141_3480x1773.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kESV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9411585d-8ac6-4001-9926-c110d4222141_3480x1773.png 424w, https://substackcdn.com/image/fetch/$s_!kESV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9411585d-8ac6-4001-9926-c110d4222141_3480x1773.png 848w, https://substackcdn.com/image/fetch/$s_!kESV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9411585d-8ac6-4001-9926-c110d4222141_3480x1773.png 1272w, https://substackcdn.com/image/fetch/$s_!kESV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9411585d-8ac6-4001-9926-c110d4222141_3480x1773.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kESV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9411585d-8ac6-4001-9926-c110d4222141_3480x1773.png" width="1456" height="742" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9411585d-8ac6-4001-9926-c110d4222141_3480x1773.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:742,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:677065,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9411585d-8ac6-4001-9926-c110d4222141_3480x1773.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_!kESV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9411585d-8ac6-4001-9926-c110d4222141_3480x1773.png 424w, https://substackcdn.com/image/fetch/$s_!kESV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9411585d-8ac6-4001-9926-c110d4222141_3480x1773.png 848w, https://substackcdn.com/image/fetch/$s_!kESV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9411585d-8ac6-4001-9926-c110d4222141_3480x1773.png 1272w, https://substackcdn.com/image/fetch/$s_!kESV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9411585d-8ac6-4001-9926-c110d4222141_3480x1773.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>The point of no easy return</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3LqX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50eef6ad-4c54-43a1-97e4-6afda046c353_3484x1552.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3LqX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50eef6ad-4c54-43a1-97e4-6afda046c353_3484x1552.png 424w, https://substackcdn.com/image/fetch/$s_!3LqX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50eef6ad-4c54-43a1-97e4-6afda046c353_3484x1552.png 848w, https://substackcdn.com/image/fetch/$s_!3LqX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50eef6ad-4c54-43a1-97e4-6afda046c353_3484x1552.png 1272w, https://substackcdn.com/image/fetch/$s_!3LqX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50eef6ad-4c54-43a1-97e4-6afda046c353_3484x1552.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3LqX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50eef6ad-4c54-43a1-97e4-6afda046c353_3484x1552.png" width="1456" height="649" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/50eef6ad-4c54-43a1-97e4-6afda046c353_3484x1552.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:649,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:560281,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50eef6ad-4c54-43a1-97e4-6afda046c353_3484x1552.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_!3LqX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50eef6ad-4c54-43a1-97e4-6afda046c353_3484x1552.png 424w, https://substackcdn.com/image/fetch/$s_!3LqX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50eef6ad-4c54-43a1-97e4-6afda046c353_3484x1552.png 848w, https://substackcdn.com/image/fetch/$s_!3LqX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50eef6ad-4c54-43a1-97e4-6afda046c353_3484x1552.png 1272w, https://substackcdn.com/image/fetch/$s_!3LqX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50eef6ad-4c54-43a1-97e4-6afda046c353_3484x1552.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Migrate contains the most important operational boundary in the whole pattern: the point of no easy return. This is the moment when rollback stops being simple.</p><p>Before that point, reverting might be as easy as disabling a flag or routing reads back to the old path. After that point, the old path may no longer be trustworthy because it has stopped receiving fresh data or because new writes are only consistent in the new structure.</p><p>A concrete database example makes this clear. Suppose a system is moving from <code>full_name</code> to <code>first_name</code> and <code>last_name</code>. During dual-write, both the old and new columns stay current, so switching reads between them is still safe. Once the team stops writing to <code>full_name</code>, the old column begins to drift. At that moment, reading from it again is no longer rollback in any meaningful sense. It returns stale data.</p><p>Name this point before you reach it. Keep the observation period before it long enough to give you genuine confidence. Do not cross it under time pressure.</p><h3>Contract</h3><p>Contract is the phase where the migration stops being temporary. The old constructor is deleted. The old endpoint is removed. The old column is dropped. The compatibility view disappears. The transitional flag is torn out.</p><p>Structurally, Contract is often the simplest phase. Operationally, it is the one teams neglect most. The reason is predictable: Expand and Migrate are usually attached to visible business value. Contract is not. Nothing user-facing changes when <code>v1</code> is deleted or when <code>full_name</code> finally disappears. That makes Contract easy to postpone, easy to deprioritise, and easy to abandon.</p><p>Postponing it means carrying complexity that has already outlived its purpose. Temporary compatibility code, dual-write paths, old endpoints, and migration flags all have an ongoing cost: they add cognitive load to every future engineer who reads the code and must decide whether the old path is safe to use.</p><p>The procedural fix is simple and must happen at the start: when you open the expand change, open the contract ticket at the same time. Assign it. Give it a date. The two are one decision.</p><h2>Conventions used in the examples</h2><p>Before getting into the examples, it is worth being explicit about the conventions they follow, because those conventions are part of the teaching.</p><p>When the examples use a <strong>port</strong> and an <strong>adapter</strong>, they follow the ports-and-adapters style, also called hexagonal architecture. The port is the abstraction that expresses what the application needs. The adapter is the technology-specific implementation that fulfils it. <code>CustomerRepository</code> is the port because it names a business role. <code>SqlServerCustomerRepository</code> is the adapter because it implements that role using SQL Server. There is no <code>I</code> prefix on the port because the name already says what it does.</p><p>When the examples use a <strong>DTO</strong>, they are using an object whose only job is transport. It exists to describe the shape of data crossing a boundary. It has no business behaviour of its own. That separation matters during migration because the transport contract is often in motion while the domain model tries to remain stable.</p><p>When the examples use a <strong>value object</strong>, they are modelling a concept whose identity is its value. <code>PostalCode</code>, <code>CountryCode</code>, <code>EmailAddress</code>, and <code>CurrencyCode</code> are all better represented as value objects than as raw strings because the type itself can enforce the rules that make the value meaningful. This is one way to resist <strong>Primitive Obsession</strong>, which is the habit of representing domain concepts with raw types like <code>string</code> or <code>int</code>. A related smell is the <strong>Data Clump</strong>: a group of primitives that always travel together but have no encapsulating object. A <strong>code smell</strong> in general is a signal that suggests a potential design problem. It is not a bug, but it often indicates that a change will be harder than it should be.</p><p>The examples also apply a few rules from <strong>Object Calisthenics</strong>: one level of indentation per method, no <code>else</code> after an early return, wrapped primitives, and small entities. These are not ends in themselves but heuristics that make it harder to write code with hidden complexity. The examples also follow <strong>Interface Segregation</strong>: no client depends on methods it does not use. <code>UserProfileReader</code> and <code>UserProfileWriter</code> are two separate interfaces because their consumers have different reasons to change.</p><p>Where the examples need database evolution, they use <strong>FluentMigrator</strong>, a .NET library that defines migrations as C# code. Each migration has <code>Up()</code> and <code>Down()</code>. Migrations are versioned, executed in order, and form an auditable history of schema evolution. Where the examples need SQL mapping, they use <strong>Dapper</strong>, a micro-ORM that maps SQL results to C# objects. You write the SQL explicitly. This predictability is exactly what is needed when the schema is in transition.</p><p>The examples vary on purpose. Some involve multiple consumers. Some involve a single application, a single team, and a single codebase. What unifies them is not the number of consumers. It is the need to move through safe intermediate states while old and new shapes coexist for a while.</p><h2>Feature flags without redeployment: ConfigCat with autopoll</h2><p>Several of the examples rely on a runtime switch. That switch is not there to hide unfinished work. It is there because moving gradually is often safer than moving everything at once, and a flag that requires a deployment to change is less useful than one that does not. A live system often needs to validate a new path under real traffic before the old path can be retired, whether the old and new shapes are consumed by different clients, different live instances of the same service, or a percentage of traffic in a canary rollout.</p><p><strong>Autopoll</strong> is an evaluation mode in which the SDK periodically checks whether flag values have changed, without the application restarting or receiving an external notification. With a 30-second interval, any change in the ConfigCat dashboard reaches every running instance within that interval. No commit, no pipeline, no deployment.</p><h3>Installation</h3><pre><code><code>dotnet add package ConfigCat.Client</code></code></pre><pre><code><code>npm install configcat-js</code></code></pre><h3>Backend port</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;63c0654b-6d17-40b5-9d43-abbb5e992ef2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public record FeatureFlagKey(string Value);

public interface FeatureFlagService
{
    Task&lt;bool&gt; IsEnabledAsync(FeatureFlagKey key);
}</code></pre></div><h3>ConfigCat adapter</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;6e1fdc07-16a9-4c80-ba4d-43b827e21b9c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class ConfigCatFeatureFlagService : FeatureFlagService
{
    private readonly IConfigCatClient _configCatClient;

    public ConfigCatFeatureFlagService(IConfigCatClient configCatClient)
    {
        _configCatClient = configCatClient;
    }

    public async Task&lt;bool&gt; IsEnabledAsync(FeatureFlagKey key)
    {
        return await _configCatClient.GetValueAsync(key.Value, false);
    }
}</code></pre></div><h3>Registration</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;4a26df40-c38b-4170-a3de-50f7b2f51a3e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public static class FeatureFlagServiceRegistration
{
    private const int AutoPollIntervalInSeconds = 30;

    public static IServiceCollection AddConfigCatFeatureFlags(
        this IServiceCollection services,
        string sdkKey)
    {
        services.AddSingleton&lt;IConfigCatClient&gt;(_ =&gt;
            ConfigCatClient.Get(sdkKey, options =&gt;
            {
                options.PollingMode = PollingModes.AutoPoll(
                    pollInterval: TimeSpan.FromSeconds(AutoPollIntervalInSeconds));
            }));

        services.AddScoped&lt;FeatureFlagService, ConfigCatFeatureFlagService&gt;();

        return services;
    }
}</code></pre></div><h3>Startup</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;039d5b62-e9c8-4f3e-be6b-7b7796307d28&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">builder.Services.AddConfigCatFeatureFlags(
    sdkKey: builder.Configuration["ConfigCat:SdkKey"]!);</code></pre></div><h3>Frontend client</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;015ca347-de29-4174-b01f-3bf154ae00bc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import * as configcat from 'configcat-js'

const AutoPollIntervalInSeconds = 30

const configCatClient = configcat.getClient(
  import.meta.env.VITE_CONFIGCAT_SDK_KEY,
  configcat.PollingMode.AutoPoll,
  { pollIntervalSeconds: AutoPollIntervalInSeconds }
)</code></pre></div><h3>Branded key type</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;67308a25-0648-4748-996b-7029d58cbc36&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">export type FeatureFlagKey = string &amp; { readonly _brand: 'FeatureFlagKey' }

export function createFeatureFlagKey(value: string): FeatureFlagKey {
  return value as FeatureFlagKey
}</code></pre></div><h3>Vue composable</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;2b36a298-7fe6-463a-b131-6f8a4d5f494e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">export function useFeatureFlags() {
  async function isFlagEnabled(key: FeatureFlagKey): Promise&lt;boolean&gt; {
    return configCatClient.getValueAsync(key, false)
  }

  return { isFlagEnabled }
}</code></pre></div><p>This infrastructure is established once here and reused in each example that needs it.</p><h2>Example 1: data clump to value object</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NYic!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d70f119-f180-4333-b5ed-d2093cf6532d_3435x1943.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NYic!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d70f119-f180-4333-b5ed-d2093cf6532d_3435x1943.png 424w, https://substackcdn.com/image/fetch/$s_!NYic!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d70f119-f180-4333-b5ed-d2093cf6532d_3435x1943.png 848w, https://substackcdn.com/image/fetch/$s_!NYic!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d70f119-f180-4333-b5ed-d2093cf6532d_3435x1943.png 1272w, https://substackcdn.com/image/fetch/$s_!NYic!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d70f119-f180-4333-b5ed-d2093cf6532d_3435x1943.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NYic!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d70f119-f180-4333-b5ed-d2093cf6532d_3435x1943.png" width="1456" height="824" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5d70f119-f180-4333-b5ed-d2093cf6532d_3435x1943.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:824,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:803810,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d70f119-f180-4333-b5ed-d2093cf6532d_3435x1943.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_!NYic!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d70f119-f180-4333-b5ed-d2093cf6532d_3435x1943.png 424w, https://substackcdn.com/image/fetch/$s_!NYic!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d70f119-f180-4333-b5ed-d2093cf6532d_3435x1943.png 848w, https://substackcdn.com/image/fetch/$s_!NYic!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d70f119-f180-4333-b5ed-d2093cf6532d_3435x1943.png 1272w, https://substackcdn.com/image/fetch/$s_!NYic!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d70f119-f180-4333-b5ed-d2093cf6532d_3435x1943.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You have a class that receives six constructor parameters. Four of them always travel together: street address, city, postal code, and country code. They are validated together, displayed together, and conceptually represent one thing, but the code still treats them as four unrelated strings. Other teams use this class. You cannot update all call sites at once. How do you introduce the missing abstraction without breaking any build?</p><p>This is the problem known as a <strong>Data Clump</strong>: four positional <code>string</code> parameters with no encapsulating object, no centralised validation, and no type that prevents them from being passed in the wrong order. If the country must be a two-letter ISO code, that rule exists scattered across every place where an <code>Order</code> is constructed rather than in one place where it can be trusted.</p><p>The starting point:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;1eb97ab1-2405-4238-8586-58d77256d0c5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class Order
{
    public OrderId Identifier { get; }
    public string Street { get; }
    public string City { get; }
    public string PostalCode { get; }
    public string CountryCode { get; }
    public Money Total { get; }

    public Order(
        OrderId identifier,
        string street,
        string city,
        string postalCode,
        string countryCode,
        Money total)
    {
        Identifier = identifier;
        Street = street;
        City = city;
        PostalCode = postalCode;
        CountryCode = countryCode;
        Total = total;
    }
}</code></pre></div><h3>Expand</h3><p>First, introduce value objects for each domain concept:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;8b9540f2-17a4-415a-8a73-1f19e9629b4f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public record StreetAddress
{
    public string Value { get; }

    public StreetAddress(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
            throw new ArgumentException("Street address must not be empty.", nameof(value));

        Value = value;
    }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;c396ca5a-9bae-40c8-91c3-080693b70063&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public record CityName
{
    public string Value { get; }

    public CityName(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
            throw new ArgumentException("City name must not be empty.", nameof(value));

        Value = value;
    }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;edf0fc07-a70e-413a-9b50-66428db14a46&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public record PostalCode
{
    public string Value { get; }

    public PostalCode(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
            throw new ArgumentException("Postal code must not be empty.", nameof(value));

        Value = value;
    }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;e0f5f435-1ba5-4246-9e62-7597ebcfd36f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public record CountryCode
{
    private const int IsoAlphaTwoLength = 2;

    public string Value { get; }

    public CountryCode(string value)
    {
        if (string.IsNullOrWhiteSpace(value) || value.Length != IsoAlphaTwoLength)
            throw new ArgumentException(
                $"Country code must be exactly {IsoAlphaTwoLength} letters.",
                nameof(value));

        Value = value.ToUpperInvariant();
    }
}</code></pre></div><p>Then introduce the encapsulating object:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;b5603fe8-825f-4690-8c71-dc0539fd1933&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class ShippingAddress
{
    public StreetAddress Street { get; }
    public CityName City { get; }
    public PostalCode PostalCode { get; }
    public CountryCode Country { get; }

    public ShippingAddress(
        StreetAddress street,
        CityName city,
        PostalCode postalCode,
        CountryCode country)
    {
        Street = street;
        City = city;
        PostalCode = postalCode;
        Country = country;
    }
}</code></pre></div><p>Now make the new constructor canonical, while keeping the old one alive as a delegating compatibility path. The <code>[Obsolete]</code><strong> attribute</strong> is the mechanism for this. In .NET, <code>[Obsolete]</code> marks a member as deprecated. The compiler emits a warning at every call site that uses it. The attribute accepts a message that can include migration instructions or a link to the tracking issue. With <code>error: false</code>, the warning does not prevent compilation. That is exactly what the migrate phase needs: the system keeps working, but every IDE warns the developer that they are using something that needs updating.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;573f3dda-797e-45b5-84b8-aa30c816dc3e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class Order
{
    public OrderId Identifier { get; }
    public ShippingAddress ShippingAddress { get; }
    public Money Total { get; }

    public Order(OrderId identifier, ShippingAddress shippingAddress, Money total)
    {
        Identifier = identifier;
        ShippingAddress = shippingAddress;
        Total = total;
    }

    [Obsolete(
        "Use Order(OrderId, ShippingAddress, Money). Migration tracked in issue 142.",
        error: false)]
    public Order(
        OrderId identifier,
        string street,
        string city,
        string postalCode,
        string countryCode,
        Money total)
        : this(
            identifier,
            new ShippingAddress(
                new StreetAddress(street),
                new CityName(city),
                new PostalCode(postalCode),
                new CountryCode(countryCode)),
            total)
    {
    }

    public string Street =&gt; ShippingAddress.Street.Value;
    public string City =&gt; ShippingAddress.City.Value;
    public string PostalCode =&gt; ShippingAddress.PostalCode.Value;
    public string CountryCode =&gt; ShippingAddress.Country.Value;
}</code></pre></div><p>At this point, nothing breaks. Old call sites compile with a warning. New code can use <code>ShippingAddress</code> directly.</p><h3>Migrate</h3><p>Call sites move independently:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;19c15582-0f2d-46c0-861e-142b42a52771&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">var shippingAddress = new ShippingAddress(
    new StreetAddress("123 Main Street"),
    new CityName("London"),
    new PostalCode("EC1A 1BB"),
    new CountryCode("GB"));

var order = new Order(
    new OrderId(1),
    shippingAddress,
    new Money(199.99m, new CurrencyCode("GBP")));</code></pre></div><h3>Contract</h3><p>Once every caller has moved, the old constructor and the convenience accessors are removed:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;74a59166-00bd-4bbf-9a82-5a453fa474a4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class Order
{
    public OrderId Identifier { get; }
    public ShippingAddress ShippingAddress { get; }
    public Money Total { get; }

    public Order(OrderId identifier, ShippingAddress shippingAddress, Money total)
    {
        Identifier = identifier;
        ShippingAddress = shippingAddress;
        Total = total;
    }
}</code></pre></div><p>Any unmigrated call site now fails at compile time. The compiler enforces the final state. No runtime check needed, no flag, no observation period. The safety net here is the build.</p><p>The lesson here is simple: even a local refactoring becomes safer when the old and new shapes are allowed to coexist deliberately for a short time. The compiler is the cheapest possible safety net. Use it.</p><h2>Example 1.5: simplifying test builders with Parallel Change</h2><div><hr></div><p><strong>There is a more complete and better example here</strong>: <a href="https://codesai.com/posts/2025/11/simplifying-builders-parallel-change">https://codesai.com/posts/2025/11/simplifying-builders-parallel-change</a></p><div><hr></div><p>Sometimes the problem is not a database or an API. Sometimes it is a structure inside the codebase that many tests depend on, and the difficulty is not technical but organisational: too many usages to change at once, too much noise if done in one big rewrite.</p><p>You have a test suite where builders share behaviour through inheritance. The original design uses generics to preserve fluent chaining and to restrict which methods are available on each specialised builder. It works, but it is harder to read than it should be, and over time the team starts to feel that the design is compensating for something.</p><p>Then the team realises that two concepts have been conflated. What looked like different &#8220;states&#8221; of the same entity are actually different workflow commands. Once that becomes clear, the inheritance-heavy builder design no longer makes sense. Composition becomes a better fit. But hundreds of tests still use the old builders. Rewriting all of them at once would be noisy, risky, and unnecessary.</p><p>This is where Parallel Change applies to test infrastructure, and where a new migrate-phase technique appears: <strong>temporary scaffolding</strong>. Temporary scaffolding is transitional code introduced solely to let old and new internal interfaces coexist while usages migrate. Unlike the final design, it is not meant to survive. It might be an overloaded method that accepts both the old and new type, a conditional adapter that routes between two implementations, or a delegating builder that can wrap either shape. Its only purpose is to keep the build green while call sites move one at a time.</p><p>This example also illustrates something worth naming explicitly: the migration process is not always just an implementation detail. Sometimes slowing down to make a change safely is what gives the team space to see the domain more clearly. The inheritance hierarchy felt necessary before the migration. Once the team stopped to plan the transition carefully, the domain conflation became visible. That insight could have been invisible if the change had been forced through in one large rewrite.</p><h3>Before</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;3ae656ec-1ed6-488a-8920-db14faff0a4f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public abstract class ClaimBuilder&lt;TBuilder&gt;
    where TBuilder : ClaimBuilder&lt;TBuilder&gt;
{
    protected ClaimId ClaimId { get; private set; } = new ClaimId("CLM-001");
    protected CompanyId CompanyId { get; private set; } = new CompanyId("COMP-001");
    protected string Description { get; private set; } = "Broken window";
    protected ClaimStatus Status { get; private set; } = ClaimStatus.ReadyToOpen;

    public TBuilder WithClaimId(string value)
    {
        ClaimId = new ClaimId(value);
        return Self();
    }

    public TBuilder WithCompanyId(string value)
    {
        CompanyId = new CompanyId(value);
        return Self();
    }

    public TBuilder DescribedAs(string value)
    {
        Description = value;
        return Self();
    }

    public TBuilder WithStatus(ClaimStatus status)
    {
        Status = status;
        return Self();
    }

    protected ClaimData BuildClaimData()
    {
        return new ClaimData(ClaimId, CompanyId, Description, Status);
    }

    protected TBuilder Self() =&gt; (TBuilder)this;
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;817f4625-f7fb-4581-8fe1-d8297f0f0102&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class ReadyToOpenClaimBuilder : ClaimBuilder&lt;ReadyToOpenClaimBuilder&gt;
{
    private Company company = Company.Default();

    public ReadyToOpenClaimBuilder InCompany(Company value)
    {
        company = value;
        return this;
    }

    public ReadyToOpenClaim Build()
    {
        return new ReadyToOpenClaim(BuildClaimData(), company);
    }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;a6e25e56-9cbd-45b7-8aa1-b89d51f3cfeb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class OpenButNotNotifiedClaimBuilder : ClaimBuilder&lt;OpenButNotNotifiedClaimBuilder&gt;
{
    private ClaimReferenceInCompany reference = new ClaimReferenceInCompany("REF-001");

    public OpenButNotNotifiedClaimBuilder WithReference(string value)
    {
        reference = new ClaimReferenceInCompany(value);
        return this;
    }

    public OpenButNotNotifiedClaim Build()
    {
        return new OpenButNotNotifiedClaim(BuildClaimData(), reference);
    }
}</code></pre></div><h3>The domain insight</h3><p><code>ReadyToOpenClaim</code> and <code>OpenButNotNotifiedClaim</code> are not domain states in any useful sense. They are workflow commands: <code>OpenClaimCommand</code> and <code>NotifyOpenedClaimCommand</code>. <code>ClaimData</code> becomes <code>Claim</code>. The inheritance hierarchy was compensating for a conceptual conflation between an entity&#8217;s state and the commands that transition it.</p><p>Once that is clear, the target design becomes simpler: command builders compose a plain <code>ClaimBuilder</code> instead of inheriting from a generic base builder.</p><h3>Expand</h3><p>Introduce the new plain builder and the new composition-based command builder without deleting anything:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;682bed5a-35ef-4aae-8f36-1ed9f013e63f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class ClaimBuilder
{
    private ClaimId claimId = new ClaimId("CLM-001");
    private CompanyId companyId = new CompanyId("COMP-001");
    private string description = "Broken window";
    private ClaimStatus status = ClaimStatus.ReadyToOpen;

    public ClaimBuilder WithClaimId(string value)
    {
        claimId = new ClaimId(value);
        return this;
    }

    public ClaimBuilder WithCompanyId(string value)
    {
        companyId = new CompanyId(value);
        return this;
    }

    public ClaimBuilder DescribedAs(string value)
    {
        description = value;
        return this;
    }

    public ClaimBuilder WithStatus(ClaimStatus value)
    {
        status = value;
        return this;
    }

    public Claim Build()
    {
        return new Claim(claimId, companyId, description, status);
    }
}</code></pre></div><p>The <code>OpenClaimCommandBuilder</code> accepts both the new <code>ClaimBuilder</code> and the legacy <code>ReadyToOpenClaimBuilder</code> through overloaded <code>Of()</code> methods. This is the scaffolding.</p><p>The field <code>claimBuilder</code> is never null: it starts as <code>new ClaimBuilder()</code>, which acts as a Null Object. When <code>Of(ClaimBuilder)</code> is called, it replaces that default. When <code>Of(ReadyToOpenClaimBuilder)</code> is called, it adapts the legacy builder into a <code>ClaimBuilder</code> immediately, at the call site, not in <code>Build()</code>. This means <code>Build()</code> has a single unconditional path with no null checks, no branching, and no <code>throw</code>. The scaffolding is in the overload, not in the assembly step.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;f23dd39c-97af-4b12-a913-a7d75e031993&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class OpenClaimCommandBuilder
{
    private Company company = Company.Default();
    private ClaimBuilder claimBuilder = new ClaimBuilder();

    public OpenClaimCommandBuilder InCompany(Company value)
    {
        company = value;
        return this;
    }

    public OpenClaimCommandBuilder Of(ClaimBuilder builder)
    {
        claimBuilder = builder;
        return this;
    }

    public OpenClaimCommandBuilder Of(ReadyToOpenClaimBuilder legacyBuilder)
    {
        var legacyClaim = legacyBuilder.Build();
        claimBuilder = new ClaimBuilder()
            .WithClaimId(legacyClaim.Data.ClaimId.Value)
            .WithCompanyId(legacyClaim.Data.CompanyId.Value)
            .DescribedAs(legacyClaim.Data.Description)
            .WithStatus(legacyClaim.Data.Status);
        return this;
    }

    public OpenClaimCommand Build()
    {
        var claim = claimBuilder
            .WithStatus(ClaimStatus.ReadyToOpen)
            .Build();

        return new OpenClaimCommand(claim, company);
    }
}</code></pre></div><p>This is not the final design. It is scaffolding. Its purpose is narrow: let old and new builders coexist while tests move independently.</p><h3>Migrate</h3><p>Tests move one at a time. Before:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;efa26bcd-c34e-4cca-8fa3-238a6b75fd37&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[Fact]
public void Opens_a_claim_for_a_company()
{
    var company = Company.Default();

    var command = new ReadyToOpenClaimBuilder()
        .WithClaimId("CLM-123")
        .DescribedAs("Broken door")
        .InCompany(company)
        .Build();

    command.Company.Should().Be(company);
}</code></pre></div><p>After:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;9642e7c6-a74d-40bb-8395-858268daf579&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[Fact]
public void Opens_a_claim_for_a_company()
{
    var company = Company.Default();

    var command = new OpenClaimCommandBuilder()
        .InCompany(company)
        .Of(new ClaimBuilder()
            .WithClaimId("CLM-123")
            .DescribedAs("Broken door"))
        .Build();

    command.Company.Should().Be(company);
}</code></pre></div><p>Nothing forces every test to move in one change. The scaffolding keeps everything green throughout.</p><h3>Contract</h3><p>Once all usages of <code>ReadyToOpenClaimBuilder</code> and <code>OpenButNotNotifiedClaimBuilder</code> are gone, delete the legacy builders, the overloaded <code>Of()</code> methods that accept them, and the conditional dispatch logic. The final <code>OpenClaimCommandBuilder</code> is clean:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;9fc973fb-4ce3-4280-9f1c-4082ca8f76d2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class OpenClaimCommandBuilder
{
    private Company company = Company.Default();
    private ClaimBuilder claimBuilder = new ClaimBuilder();

    public OpenClaimCommandBuilder InCompany(Company value)
    {
        company = value;
        return this;
    }

    public OpenClaimCommandBuilder Of(ClaimBuilder builder)
    {
        claimBuilder = builder;
        return this;
    }

    public OpenClaimCommand Build()
    {
        var claim = claimBuilder
            .WithStatus(ClaimStatus.ReadyToOpen)
            .Build();

        return new OpenClaimCommand(claim, company);
    }
}</code></pre></div><p>The lesson is that Parallel Change is not only a technique for protecting live production systems. It applies equally well to internal structures like test builders, and the careful approach to migration is sometimes what gives the team the space to find a better design.</p><h2>Example 2: splitting <code>full_name</code> into <code>first_name</code> and <code>last_name</code></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GC88!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be3ccf9-78de-498d-a019-50e66fd4ef21_3435x1894.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GC88!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be3ccf9-78de-498d-a019-50e66fd4ef21_3435x1894.png 424w, https://substackcdn.com/image/fetch/$s_!GC88!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be3ccf9-78de-498d-a019-50e66fd4ef21_3435x1894.png 848w, https://substackcdn.com/image/fetch/$s_!GC88!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be3ccf9-78de-498d-a019-50e66fd4ef21_3435x1894.png 1272w, https://substackcdn.com/image/fetch/$s_!GC88!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be3ccf9-78de-498d-a019-50e66fd4ef21_3435x1894.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GC88!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be3ccf9-78de-498d-a019-50e66fd4ef21_3435x1894.png" width="1456" height="803" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6be3ccf9-78de-498d-a019-50e66fd4ef21_3435x1894.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:803,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:841612,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be3ccf9-78de-498d-a019-50e66fd4ef21_3435x1894.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_!GC88!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be3ccf9-78de-498d-a019-50e66fd4ef21_3435x1894.png 424w, https://substackcdn.com/image/fetch/$s_!GC88!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be3ccf9-78de-498d-a019-50e66fd4ef21_3435x1894.png 848w, https://substackcdn.com/image/fetch/$s_!GC88!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be3ccf9-78de-498d-a019-50e66fd4ef21_3435x1894.png 1272w, https://substackcdn.com/image/fetch/$s_!GC88!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be3ccf9-78de-498d-a019-50e66fd4ef21_3435x1894.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A <code>customers</code> table has a <code>full_name</code> column. The business needs separate first and last name fields. You have a live system with real writes happening continuously. If you can only run one migration per deployment and cannot take a maintenance window, what is the minimum sequence of migrations that gets you to the target state without data loss?</p><p>The starting schema:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;9065ab5a-0cc7-4800-a6cd-7ac291ad1033&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    full_name   NVARCHAR(200) NOT NULL,
    email       NVARCHAR(200) NOT NULL
);</code></pre></div><p><strong>Dual-write</strong> is the practice of writing the same data to two locations simultaneously during a migration. Every <code>INSERT</code> and every <code>UPDATE</code> writes to <code>full_name</code>, <code>first_name</code>, and <code>last_name</code> at the same time. This guarantees that both read paths return current data regardless of which is active at any moment.</p><p><strong>Batched backfill</strong> is the process of retroactively populating existing rows that were created before the new columns existed. On a table with millions of rows, a single-transaction mass <code>UPDATE</code> can lock the table for minutes and block production writes. Running it in batches of a few hundred rows, with a short pause between each batch, leaves room for the system&#8217;s normal activity.</p><p>The domain model is updated to carry the target truth from the start:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;9f5164bb-9ede-45c4-8716-b9a6d8d4084f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public record CustomerId(int Value);</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;c2cc5a47-f5eb-4103-bb58-7bf8bd0ecb5f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class CustomerName
{
    public string FirstName { get; }
    public string LastName { get; }
    public string FullName =&gt; $"{FirstName} {LastName}".Trim();

    public CustomerName(string firstName, string lastName)
    {
        if (string.IsNullOrWhiteSpace(firstName))
            throw new ArgumentException("First name must not be empty.", nameof(firstName));

        if (string.IsNullOrWhiteSpace(lastName))
            throw new ArgumentException("Last name must not be empty.", nameof(lastName));

        FirstName = firstName;
        LastName = lastName;
    }

    public static CustomerName SplitFromFullName(string fullName)
    {
        var parts = fullName.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
        var firstName = parts.ElementAtOrDefault(0) ?? string.Empty;
        var lastName = parts.ElementAtOrDefault(1) ?? string.Empty;
        return new CustomerName(firstName, lastName);
    }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;3bbae76a-4160-42ac-b9bf-bc60d833e660&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public record Customer(CustomerId Identifier, CustomerName Name, EmailAddress Email);</code></pre></div><h3>Expand: Migration 001</h3><p>Add the new columns without removing <code>full_name</code>. The old column stays intact and reads continue working:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;3c0a8baa-d628-4e67-85ce-55ad5f3601f7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[Migration(20240301001, "Expand: add first_name and last_name alongside full_name")]
public sealed class AddSplitNameColumnsToCustomers : Migration
{
    private const string CustomersTable = "customers";
    private const string FirstNameColumn = "first_name";
    private const string LastNameColumn = "last_name";
    private const int NameMaxLength = 100;

    public override void Up()
    {
        Alter.Table(CustomersTable)
            .AddColumn(FirstNameColumn).AsString(NameMaxLength).Nullable()
            .AddColumn(LastNameColumn).AsString(NameMaxLength).Nullable();
    }

    public override void Down()
    {
        Delete.Column(FirstNameColumn).FromTable(CustomersTable);
        Delete.Column(LastNameColumn).FromTable(CustomersTable);
    }
}</code></pre></div><p>All writes now use dual-write:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;26133b0a-faed-4016-904f-27f943cfd2bb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">internal static class CustomerSqlQueries
{
    internal const string InsertNewCustomer =
        "INSERT INTO customers (customer_id, full_name, first_name, last_name, email) " +
        "VALUES (@CustomerId, @FullName, @FirstName, @LastName, @Email)";

    internal const string UpdateExistingCustomer =
        "UPDATE customers " +
        "SET full_name = @FullName, first_name = @FirstName, last_name = @LastName " +
        "WHERE customer_id = @CustomerId";

    internal const string FindByIdUsingSplitColumns =
        "SELECT customer_id AS CustomerId, first_name AS FirstName, last_name AS LastName, email AS Email " +
        "FROM customers WHERE customer_id = @CustomerId";

    internal const string FindByIdUsingFullName =
        "SELECT customer_id AS CustomerId, full_name AS FullName, email AS Email " +
        "FROM customers WHERE customer_id = @CustomerId";
}</code></pre></div><h3>Migrate: Migration 002</h3><p>Backfill existing rows in batches, then enforce <code>NOT NULL</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;662bc464-69c7-48b2-9857-d589f28b8651&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[Migration(20240308002, "Migrate: backfill first_name and last_name, enforce NOT NULL")]
public sealed class BackfillAndConstrainSplitNameColumns : Migration
{
    private const string CustomersTable = "customers";
    private const string FirstNameColumn = "first_name";
    private const string LastNameColumn = "last_name";
    private const string FullNameColumn = "full_name";
    private const int RowsPerBatch = 500;
    private const string WaitBetweenBatches = "00:00:00.050";

    public override void Up()
    {
        Execute.Sql(BuildBackfillStatement());

        Alter.Table(CustomersTable)
            .AlterColumn(FirstNameColumn).AsString(100).NotNullable()
            .AlterColumn(LastNameColumn).AsString(100).NotNullable();
    }

    public override void Down()
    {
        Alter.Table(CustomersTable)
            .AlterColumn(FirstNameColumn).AsString(100).Nullable()
            .AlterColumn(LastNameColumn).AsString(100).Nullable();

        Execute.Sql(
            $"UPDATE {CustomersTable} SET {FirstNameColumn} = NULL, {LastNameColumn} = NULL;");
    }

    private static string BuildBackfillStatement() =&gt; $@"
        WHILE EXISTS (
            SELECT 1 FROM {CustomersTable}
            WHERE {FirstNameColumn} IS NULL OR {LastNameColumn} IS NULL
        )
        BEGIN
            UPDATE TOP ({RowsPerBatch}) {CustomersTable}
            SET
                {FirstNameColumn} = LTRIM(RTRIM(
                    CASE
                        WHEN CHARINDEX(' ', {FullNameColumn}) &gt; 0
                        THEN LEFT({FullNameColumn}, CHARINDEX(' ', {FullNameColumn}) - 1)
                        ELSE {FullNameColumn}
                    END)),
                {LastNameColumn} = LTRIM(RTRIM(
                    CASE
                        WHEN CHARINDEX(' ', {FullNameColumn}) &gt; 0
                        THEN SUBSTRING(
                            {FullNameColumn},
                            CHARINDEX(' ', {FullNameColumn}) + 1,
                            LEN({FullNameColumn}))
                        ELSE ''
                    END))
            WHERE {FirstNameColumn} IS NULL OR {LastNameColumn} IS NULL;

            WAITFOR DELAY '{WaitBetweenBatches}';
        END;";
}</code></pre></div><p>Reads can now switch behind a flag:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;f72e6ab2-fa0e-480c-996f-823715d42070&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public static class CustomerFeatureFlagKeys
{
    public static readonly FeatureFlagKey ReadsUseSplitNameColumns =
        new FeatureFlagKey("customer.reads-use-split-name-columns");
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;ab99f826-5356-47f5-8749-64dbfc681eff&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class SqlServerCustomerRepository : CustomerRepository
{
    private readonly DatabaseConnectionFactory _connectionFactory;
    private readonly FeatureFlagService _featureFlagService;

    public SqlServerCustomerRepository(
        DatabaseConnectionFactory connectionFactory,
        FeatureFlagService featureFlagService)
    {
        _connectionFactory = connectionFactory;
        _featureFlagService = featureFlagService;
    }

    public async Task&lt;Customer&gt; FindByIdAsync(CustomerId identifier)
    {
        var readsUseSplitColumns =
            await _featureFlagService.IsEnabledAsync(CustomerFeatureFlagKeys.ReadsUseSplitNameColumns);

        return readsUseSplitColumns
            ? await FindReadingSplitColumns(identifier)
            : await FindReadingFullName(identifier);
    }

    private async Task&lt;Customer&gt; FindReadingSplitColumns(CustomerId identifier)
    {
        using var connection = _connectionFactory.CreateOpenConnection();

        var row = await connection.QuerySingleAsync&lt;CustomerSplitNameRow&gt;(
            CustomerSqlQueries.FindByIdUsingSplitColumns,
            new { CustomerId = identifier.Value });

        return new Customer(
            new CustomerId(row.CustomerId),
            new CustomerName(row.FirstName, row.LastName),
            new EmailAddress(row.Email));
    }

    private async Task&lt;Customer&gt; FindReadingFullName(CustomerId identifier)
    {
        using var connection = _connectionFactory.CreateOpenConnection();

        var row = await connection.QuerySingleAsync&lt;CustomerFullNameRow&gt;(
            CustomerSqlQueries.FindByIdUsingFullName,
            new { CustomerId = identifier.Value });

        return new Customer(
            new CustomerId(row.CustomerId),
            CustomerName.SplitFromFullName(row.FullName),
            new EmailAddress(row.Email));
    }
}</code></pre></div><p>The point of no easy return in this example is when the team stops writing to <code>full_name</code>. Up to that moment, routing reads back to the old column is still safe. After it, the old column goes stale and returning to it is no longer rollback. It is returning incorrect data.</p><p>Enable the flag for a small percentage of traffic first. Validate that the split reads produce correct results. Then widen the rollout. Cross the point of no easy return only after the new path has proved trustworthy in production.</p><h3>Contract: Migration 003</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;6bffdef3-dc7a-48cd-821b-c557e62308b1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[Migration(20240322003, "Contract: remove full_name after all readers use split columns")]
public sealed class RemoveFullNameColumnFromCustomers : Migration
{
    private const string CustomersTable = "customers";
    private const string FullNameColumn = "full_name";

    public override void Up()
    {
        Delete.Column(FullNameColumn).FromTable(CustomersTable);
    }

    public override void Down()
    {
        Alter.Table(CustomersTable)
            .AddColumn(FullNameColumn).AsString(200).Nullable();

        Execute.Sql(
            $"UPDATE {CustomersTable} " +
            $"SET {FullNameColumn} = LTRIM(RTRIM(first_name + ' ' + last_name));");
    }
}</code></pre></div><p>The repository now has a single read path. Remove the flag check and the legacy query:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;2b7965d5-9c9a-414f-9965-d93ba652d53f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public async Task&lt;Customer&gt; FindByIdAsync(CustomerId identifier)
{
    using var connection = _connectionFactory.CreateOpenConnection();

    var row = await connection.QuerySingleAsync&lt;CustomerSplitNameRow&gt;(
        CustomerSqlQueries.FindByIdUsingSplitColumns,
        new { CustomerId = identifier.Value });

    return new Customer(
        new CustomerId(row.CustomerId),
        new CustomerName(row.FirstName, row.LastName),
        new EmailAddress(row.Email));
}</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rXbv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a1d1353-960e-4438-b530-3c4a3a2acbe4_432x1940.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rXbv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a1d1353-960e-4438-b530-3c4a3a2acbe4_432x1940.png 424w, https://substackcdn.com/image/fetch/$s_!rXbv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a1d1353-960e-4438-b530-3c4a3a2acbe4_432x1940.png 848w, https://substackcdn.com/image/fetch/$s_!rXbv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a1d1353-960e-4438-b530-3c4a3a2acbe4_432x1940.png 1272w, https://substackcdn.com/image/fetch/$s_!rXbv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a1d1353-960e-4438-b530-3c4a3a2acbe4_432x1940.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rXbv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a1d1353-960e-4438-b530-3c4a3a2acbe4_432x1940.png" width="156" height="700.5555555555555" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a1d1353-960e-4438-b530-3c4a3a2acbe4_432x1940.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1940,&quot;width&quot;:432,&quot;resizeWidth&quot;:156,&quot;bytes&quot;:120987,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a1d1353-960e-4438-b530-3c4a3a2acbe4_432x1940.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_!rXbv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a1d1353-960e-4438-b530-3c4a3a2acbe4_432x1940.png 424w, https://substackcdn.com/image/fetch/$s_!rXbv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a1d1353-960e-4438-b530-3c4a3a2acbe4_432x1940.png 848w, https://substackcdn.com/image/fetch/$s_!rXbv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a1d1353-960e-4438-b530-3c4a3a2acbe4_432x1940.png 1272w, https://substackcdn.com/image/fetch/$s_!rXbv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a1d1353-960e-4438-b530-3c4a3a2acbe4_432x1940.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The real trick is not adding two columns. The real trick is maintaining trust in both shapes while the system passes through the transition, and knowing exactly when that trust no longer goes in both directions.</p><h2>Example 3: full stack with a BFF, a breaking API change, and an uncontrolled consumer</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yokR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e952142-d8a3-4134-ac25-d27036ec146c_3435x1857.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yokR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e952142-d8a3-4134-ac25-d27036ec146c_3435x1857.png 424w, https://substackcdn.com/image/fetch/$s_!yokR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e952142-d8a3-4134-ac25-d27036ec146c_3435x1857.png 848w, https://substackcdn.com/image/fetch/$s_!yokR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e952142-d8a3-4134-ac25-d27036ec146c_3435x1857.png 1272w, https://substackcdn.com/image/fetch/$s_!yokR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e952142-d8a3-4134-ac25-d27036ec146c_3435x1857.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yokR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e952142-d8a3-4134-ac25-d27036ec146c_3435x1857.png" width="1456" height="787" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9e952142-d8a3-4134-ac25-d27036ec146c_3435x1857.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:787,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:717663,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e952142-d8a3-4134-ac25-d27036ec146c_3435x1857.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_!yokR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e952142-d8a3-4134-ac25-d27036ec146c_3435x1857.png 424w, https://substackcdn.com/image/fetch/$s_!yokR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e952142-d8a3-4134-ac25-d27036ec146c_3435x1857.png 848w, https://substackcdn.com/image/fetch/$s_!yokR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e952142-d8a3-4134-ac25-d27036ec146c_3435x1857.png 1272w, https://substackcdn.com/image/fetch/$s_!yokR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e952142-d8a3-4134-ac25-d27036ec146c_3435x1857.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Your BFF exposes </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;7b8f3723-eb21-4ae2-9f97-a923812cf300&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">GET /api/v1/user-profiles/:id returning 
{ "fullName": "Maria Garcia" }</code></pre></div><p>The Vue frontend uses it. A mobile app also uses it. The mobile team tells you they can migrate in six weeks. You need <code>firstName</code> and <code>lastName</code> in the database and in the API response. The change to the response shape is a breaking change: the old field disappears in the new contract. The old contract must remain alive for the mobile app. The frontend and the backend deploy independently. When is the old contract safe to delete? Who decides? How do you prevent the old endpoint from living in the codebase forever with nobody accountable for removing it?</p><p>A <strong>Backend for Frontend</strong>, or BFF, is a backend service designed specifically to serve the needs of a particular user interface. Instead of a generic API consumed by multiple clients, the BFF adapts data to the exact contract the frontend needs: the shape of the objects, the fields it includes, the level of aggregation.</p><p><strong>API versioning</strong> means maintaining two distinct routes that expose different contracts for the same resource. Version 1 honours the old contract. Version 2 exposes the new one. No consumer is forced to migrate. The old contract does not disappear until nothing depends on it.</p><p>The database layer follows the same three-migration sequence as Example 2. By the time the API changes are deployed, <code>first_name</code> and <code>last_name</code> are already present and backfilled. The BFF always reads split fields. The <code>v1</code> endpoint computes <code>fullName</code> from them.</p><h3>Expand</h3><p>The domain model and the two DTOs:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;3b0e3c63-73b7-488b-b166-46cd3c50a5d0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public record UserId(int Value);
public record FirstName(string Value);
public record LastName(string Value);

public sealed class UserName
{
    public FirstName FirstName { get; }
    public LastName LastName { get; }
    public string FullName =&gt; $"{FirstName.Value} {LastName.Value}".Trim();

    public UserName(FirstName firstName, LastName lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }
}

public record UserProfile(UserId Identifier, UserName Name);</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;7dca329c-48b2-41a5-9f75-b73a7bdc9e37&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class UserProfileV1Response
{
    public string FullName { get; init; } = string.Empty;

    public static UserProfileV1Response FromDomain(UserName name) =&gt;
        new() { FullName = name.FullName };
}

public sealed class UserProfileV2Response
{
    public string FirstName { get; init; } = string.Empty;
    public string LastName { get; init; } = string.Empty;

    public static UserProfileV2Response FromDomain(UserName name) =&gt;
        new()
        {
            FirstName = name.FirstName.Value,
            LastName = name.LastName.Value
        };
}</code></pre></div><p>The controller serves both versions from the same domain model:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;f4e5e2cb-5126-4899-aa99-4adfb8f509c8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[ApiController]
public sealed class UserProfileController : ControllerBase
{
    private readonly UserProfileReader _reader;

    public UserProfileController(UserProfileReader reader)
    {
        _reader = reader;
    }

    [HttpGet("api/v1/user-profiles/{userId:int}")]
    public async Task&lt;IActionResult&gt; GetUserProfileV1(int userId)
    {
        var profile = await _reader.FindProfileByIdAsync(new UserId(userId));
        return Ok(UserProfileV1Response.FromDomain(profile.Name));
    }

    [HttpGet("api/v2/user-profiles/{userId:int}")]
    public async Task&lt;IActionResult&gt; GetUserProfileV2(int userId)
    {
        var profile = await _reader.FindProfileByIdAsync(new UserId(userId));
        return Ok(UserProfileV2Response.FromDomain(profile.Name));
    }

    [HttpPut("api/v1/user-profiles/{userId:int}")]
    public async Task&lt;IActionResult&gt; UpdateUserProfileV1(int userId, [FromBody] UserProfileV1Request request)
    {
        var name = UserName.FromFullName(request.FullName);
        await _writer.UpdateNameAsync(new UserId(userId), name);
        return NoContent();
    }

    [HttpPut("api/v2/user-profiles/{userId:int}")]
    public async Task&lt;IActionResult&gt; UpdateUserProfileV2(int userId, [FromBody] UserProfileV2Request request)
    {
        var name = new UserName(new FirstName(request.FirstName), new LastName(request.LastName));
        await _writer.UpdateNameAsync(new UserId(userId), name);
        return NoContent();
    }
}</code></pre></div><p>At this point, nothing breaks. Mobile keeps consuming <code>v1</code>. New clients can use <code>v2</code> immediately.</p><h3>Migrate</h3><p>The flag and the composable on the Vue side:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;3eaee2d7-e6a9-46a2-87af-9a4000ddf434&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import { createFeatureFlagKey } from '@/featureFlags/FeatureFlagKey'

export const UserProfileFeatureFlags = {
  useV2Endpoint: createFeatureFlagKey('user-profile.use-v2-endpoint')
} as const</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;d3e62c7d-1f72-4b9f-9399-6a2bc7648f93&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import { reactive, onMounted, ref } from 'vue'
import { useFeatureFlags } from '@/featureFlags/useFeatureFlags'
import { UserProfileFeatureFlags } from '@/featureFlags/userProfileFeatureFlags'

export function useUserProfileForm(userId: number) {
  const { isFlagEnabled } = useFeatureFlags()
  const useV2EndpointEnabled = ref(false)

  const form = reactive({
    fullName: '',
    firstName: '',
    lastName: ''
  })

  async function loadProfile(): Promise&lt;void&gt; {
    useV2EndpointEnabled.value = await isFlagEnabled(UserProfileFeatureFlags.useV2Endpoint)

    if (useV2EndpointEnabled.value) {
      const response = await fetch(`/api/v2/user-profiles/${userId}`)
      const data: { firstName: string; lastName: string } = await response.json()
      form.firstName = data.firstName
      form.lastName = data.lastName
      return
    }

    const response = await fetch(`/api/v1/user-profiles/${userId}`)
    const data: { fullName: string } = await response.json()
    form.fullName = data.fullName
  }

  onMounted(loadProfile)

  return { form, useV2EndpointEnabled }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;d788455a-9717-4973-82fe-b94b43493d05&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">&lt;template&gt;
  &lt;form&gt;
    &lt;template v-if="useV2EndpointEnabled"&gt;
      &lt;label for="first-name"&gt;First name&lt;/label&gt;
      &lt;input id="first-name" v-model="form.firstName" type="text" /&gt;
      &lt;label for="last-name"&gt;Last name&lt;/label&gt;
      &lt;input id="last-name" v-model="form.lastName" type="text" /&gt;
    &lt;/template&gt;

    &lt;template v-else&gt;
      &lt;label for="full-name"&gt;Full name&lt;/label&gt;
      &lt;input id="full-name" v-model="form.fullName" type="text" /&gt;
    &lt;/template&gt;
  &lt;/form&gt;
&lt;/template&gt;

&lt;script setup lang="ts"&gt;
import { useUserProfileForm } from '@/composables/useUserProfileForm'

const props = defineProps&lt;{ userId: number }&gt;()
const { form, useV2EndpointEnabled } = useUserProfileForm(props.userId)
&lt;/script&gt;</code></pre></div><p>Enable the flag in the ConfigCat dashboard. The Vue frontend moves to <code>v2</code>. The mobile app keeps calling <code>v1</code>. Both receive correct data. No coordinated deployment needed.</p><h3>Contract</h3><p>When the mobile team confirms their migration is complete, delete the <code>v1</code> endpoints. Update the request DTO to accept only the new shape. Remove the flag check from the composable. Remove the <code>v-else</code> branch from the component.</p><p>A note on accountability: when <code>v2</code> is created, open the contract ticket at the same time. Assign it. Date it to the mobile team&#8217;s six-week timeline. Without this step, <code>v1</code> lives in the codebase indefinitely, nobody knows whether anything still depends on it, and removing it becomes progressively harder to justify in a sprint.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5OJA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6dc33b-2779-452c-926e-e6c48fb57b34_1962x1734.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5OJA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6dc33b-2779-452c-926e-e6c48fb57b34_1962x1734.png 424w, https://substackcdn.com/image/fetch/$s_!5OJA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6dc33b-2779-452c-926e-e6c48fb57b34_1962x1734.png 848w, https://substackcdn.com/image/fetch/$s_!5OJA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6dc33b-2779-452c-926e-e6c48fb57b34_1962x1734.png 1272w, https://substackcdn.com/image/fetch/$s_!5OJA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6dc33b-2779-452c-926e-e6c48fb57b34_1962x1734.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5OJA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6dc33b-2779-452c-926e-e6c48fb57b34_1962x1734.png" width="1456" height="1287" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e6dc33b-2779-452c-926e-e6c48fb57b34_1962x1734.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1287,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:169169,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6dc33b-2779-452c-926e-e6c48fb57b34_1962x1734.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_!5OJA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6dc33b-2779-452c-926e-e6c48fb57b34_1962x1734.png 424w, https://substackcdn.com/image/fetch/$s_!5OJA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6dc33b-2779-452c-926e-e6c48fb57b34_1962x1734.png 848w, https://substackcdn.com/image/fetch/$s_!5OJA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6dc33b-2779-452c-926e-e6c48fb57b34_1962x1734.png 1272w, https://substackcdn.com/image/fetch/$s_!5OJA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6dc33b-2779-452c-926e-e6c48fb57b34_1962x1734.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Versioning is a bridge, not a destination. The value it creates is time. That time belongs to the mobile team, not to the endpoint.</p><h2>Example 4: renaming a German database table and columns to English</h2><p>You have a table named <code>benutzer</code> with columns in German: <code>vorname</code>, <code>nachname</code>, <code>strasse</code>, <code>ort</code>, <code>postleitzahl</code>, <code>erstellt_am</code>. Three applications read from it. None of them share a deployment cycle. You want to rename everything to English. You cannot lock the table. You cannot ask the three applications to deploy on the same day. How do you rename a database table without coordination?</p><p>A <strong>compatibility view</strong> is a SQL view created during a migration that exposes new column names over the old table structure. New code reads from the view with English names. Old code reads from the table with German names. Both access the same underlying data without duplication. It is the cheapest possible expand mechanism for a rename because it involves no data copying and no schema alteration, only a view definition.</p><p><code>sp_rename</code> is a SQL Server stored procedure for renaming database objects at runtime. Renaming a table with <code>sp_rename</code> does not require recreating it or copying data. It is a metadata operation and takes effect immediately. It is used in the contract phase to perform the real rename after nothing depends on the old name.</p><p>The starting schema:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;945c9d93-1e4d-42a5-a0c4-6b824e8d29ee&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">CREATE TABLE benutzer (
    benutzer_id  INT PRIMARY KEY,
    vorname      NVARCHAR(100) NOT NULL,
    nachname     NVARCHAR(100) NOT NULL,
    strasse      NVARCHAR(200) NOT NULL,
    ort          NVARCHAR(100) NOT NULL,
    postleitzahl NVARCHAR(20)  NOT NULL,
    erstellt_am  DATETIME2     NOT NULL DEFAULT GETUTCDATE()
);</code></pre></div><h3>Expand: Migration 001</h3><p>Create the compatibility view. New code reads from it immediately:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;c69ce2f4-dc4c-44ef-a202-43d31164b92f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[Migration(20240301001, "Expand: create users compatibility view over benutzer")]
public sealed class CreateUsersCompatibilityView : Migration
{
    public override void Up()
    {
        Execute.Sql(@"
            CREATE VIEW users AS
            SELECT
                benutzer_id  AS user_id,
                vorname      AS first_name,
                nachname     AS last_name,
                strasse      AS street,
                ort          AS city,
                postleitzahl AS postal_code,
                erstellt_am  AS created_at
            FROM benutzer;");
    }

    public override void Down()
    {
        Execute.Sql("DROP VIEW IF EXISTS users;");
    }
}</code></pre></div><p>Old applications keep reading from <code>benutzer</code> unchanged. New code reads from <code>users</code> with English names. Both hit the same rows.</p><h3>Migrate: Migrations 002 and 003</h3><p>Add English columns alongside the German ones:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;4e8c0314-f8d0-4fdc-9161-36062e6ad0db&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[Migration(20240308002, "Migrate: add English columns to benutzer")]
public sealed class AddEnglishColumnsToBenutzer : Migration
{
    private const string BenutzerTable = "benutzer";
    private const string FirstNameColumn = "first_name";
    private const string LastNameColumn = "last_name";
    private const string StreetColumn = "street";
    private const string CityColumn = "city";
    private const string PostalCodeColumn = "postal_code";

    public override void Up()
    {
        Alter.Table(BenutzerTable)
            .AddColumn(FirstNameColumn).AsString(100).Nullable()
            .AddColumn(LastNameColumn).AsString(100).Nullable()
            .AddColumn(StreetColumn).AsString(200).Nullable()
            .AddColumn(CityColumn).AsString(100).Nullable()
            .AddColumn(PostalCodeColumn).AsString(20).Nullable();
    }

    public override void Down()
    {
        Delete.Column(FirstNameColumn).FromTable(BenutzerTable);
        Delete.Column(LastNameColumn).FromTable(BenutzerTable);
        Delete.Column(StreetColumn).FromTable(BenutzerTable);
        Delete.Column(CityColumn).FromTable(BenutzerTable);
        Delete.Column(PostalCodeColumn).FromTable(BenutzerTable);
    }
}</code></pre></div><p>Backfill and enforce <code>NOT NULL</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;c69a2262-1d6d-480c-96d2-83bde1eddf55&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[Migration(20240315003, "Migrate: backfill English columns and enforce NOT NULL")]
public sealed class BackfillEnglishColumnsInBenutzer : Migration
{
    private const string BenutzerTable = "benutzer";
    private const int RowsPerBatch = 500;
    private const string WaitBetweenBatches = "00:00:00.050";

    public override void Up()
    {
        Execute.Sql($@"
            WHILE EXISTS (
                SELECT 1 FROM {BenutzerTable}
                WHERE first_name IS NULL
            )
            BEGIN
                UPDATE TOP ({RowsPerBatch}) {BenutzerTable}
                SET
                    first_name   = vorname,
                    last_name    = nachname,
                    street       = strasse,
                    city         = ort,
                    postal_code  = postleitzahl
                WHERE first_name IS NULL;

                WAITFOR DELAY '{WaitBetweenBatches}';
            END;");

        Alter.Table(BenutzerTable)
            .AlterColumn("first_name").AsString(100).NotNullable()
            .AlterColumn("last_name").AsString(100).NotNullable()
            .AlterColumn("street").AsString(200).NotNullable()
            .AlterColumn("city").AsString(100).NotNullable()
            .AlterColumn("postal_code").AsString(20).NotNullable();
    }

    public override void Down()
    {
        Alter.Table(BenutzerTable)
            .AlterColumn("first_name").AsString(100).Nullable()
            .AlterColumn("last_name").AsString(100).Nullable()
            .AlterColumn("street").AsString(200).Nullable()
            .AlterColumn("city").AsString(100).Nullable()
            .AlterColumn("postal_code").AsString(20).Nullable();
    }
}</code></pre></div><p>All inserts and updates now use dual-write: writing to both German and English columns simultaneously.</p><h3>Contract: Migration 004</h3><p>Once nothing depends on the German names, drop the compatibility view, drop the German columns, and execute the real rename:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;71151559-450d-4039-a126-17ea06a3f0d4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[Migration(20240329004, "Contract: rename benutzer to users and drop German columns")]
public sealed class RenameBenutzerToUsersAndDropGermanColumns : Migration
{
    public override void Up()
    {
        Execute.Sql("DROP VIEW IF EXISTS users;");

        Delete.Column("vorname").FromTable("benutzer");
        Delete.Column("nachname").FromTable("benutzer");
        Delete.Column("strasse").FromTable("benutzer");
        Delete.Column("ort").FromTable("benutzer");
        Delete.Column("postleitzahl").FromTable("benutzer");

        Execute.Sql("EXEC sp_rename 'benutzer', 'users';");
        Execute.Sql("EXEC sp_rename 'users.benutzer_id', 'user_id', 'COLUMN';");
        Execute.Sql("EXEC sp_rename 'users.erstellt_am', 'created_at', 'COLUMN';");
    }

    public override void Down()
    {
        throw new NotSupportedException(
            "Rolling back a table rename requires restoring the database from backup.");
    }
}</code></pre></div><p>The <code>Down()</code> throws <code>NotSupportedException</code>. This is not laziness. It is an honest statement: once the table is renamed, reversing that change with code is dangerous because views, stored procedures, and indexes may all reference the new name. The only safe rollback at this point is a database restore. Documenting this explicitly is more useful than a <code>Down()</code> that silently leaves the schema in an inconsistent state.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YPRP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3452014f-7865-4f90-bd77-5685463468b8_2129x236.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YPRP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3452014f-7865-4f90-bd77-5685463468b8_2129x236.png 424w, https://substackcdn.com/image/fetch/$s_!YPRP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3452014f-7865-4f90-bd77-5685463468b8_2129x236.png 848w, https://substackcdn.com/image/fetch/$s_!YPRP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3452014f-7865-4f90-bd77-5685463468b8_2129x236.png 1272w, https://substackcdn.com/image/fetch/$s_!YPRP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3452014f-7865-4f90-bd77-5685463468b8_2129x236.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YPRP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3452014f-7865-4f90-bd77-5685463468b8_2129x236.png" width="1456" height="161" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3452014f-7865-4f90-bd77-5685463468b8_2129x236.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:161,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:54333,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3452014f-7865-4f90-bd77-5685463468b8_2129x236.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_!YPRP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3452014f-7865-4f90-bd77-5685463468b8_2129x236.png 424w, https://substackcdn.com/image/fetch/$s_!YPRP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3452014f-7865-4f90-bd77-5685463468b8_2129x236.png 848w, https://substackcdn.com/image/fetch/$s_!YPRP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3452014f-7865-4f90-bd77-5685463468b8_2129x236.png 1272w, https://substackcdn.com/image/fetch/$s_!YPRP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3452014f-7865-4f90-bd77-5685463468b8_2129x236.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p><p>The lesson is that the safest way to rename something is often not to rename it first. It is to create a stable compatible surface, let everything migrate to it, and only then perform the real rename when nothing depends on the old name.</p><h2>Example 5: from a hardcoded USD price to multi-currency</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!l3Jo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779faf9f-f59f-4f0e-a5f1-c5a054b4bc4a_3435x2001.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!l3Jo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779faf9f-f59f-4f0e-a5f1-c5a054b4bc4a_3435x2001.png 424w, https://substackcdn.com/image/fetch/$s_!l3Jo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779faf9f-f59f-4f0e-a5f1-c5a054b4bc4a_3435x2001.png 848w, https://substackcdn.com/image/fetch/$s_!l3Jo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779faf9f-f59f-4f0e-a5f1-c5a054b4bc4a_3435x2001.png 1272w, https://substackcdn.com/image/fetch/$s_!l3Jo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779faf9f-f59f-4f0e-a5f1-c5a054b4bc4a_3435x2001.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!l3Jo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779faf9f-f59f-4f0e-a5f1-c5a054b4bc4a_3435x2001.png" width="1456" height="848" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/779faf9f-f59f-4f0e-a5f1-c5a054b4bc4a_3435x2001.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:848,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:816450,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779faf9f-f59f-4f0e-a5f1-c5a054b4bc4a_3435x2001.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_!l3Jo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779faf9f-f59f-4f0e-a5f1-c5a054b4bc4a_3435x2001.png 424w, https://substackcdn.com/image/fetch/$s_!l3Jo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779faf9f-f59f-4f0e-a5f1-c5a054b4bc4a_3435x2001.png 848w, https://substackcdn.com/image/fetch/$s_!l3Jo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779faf9f-f59f-4f0e-a5f1-c5a054b4bc4a_3435x2001.png 1272w, https://substackcdn.com/image/fetch/$s_!l3Jo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F779faf9f-f59f-4f0e-a5f1-c5a054b4bc4a_3435x2001.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Your product card renders <code>$ price.toFixed(2)</code>. The backend returns a plain decimal. The business now needs to support EUR, GBP, and JPY. Each currency has different formatting rules. The database has no currency column. The frontend and backend teams deploy on different cycles. If you ship the backend change before the frontend is ready to use the new fields, the old <code>price</code> field must still be present in the response. If you ship the frontend change before the backend provides a currency code, it has nothing to format with. What is the order? What does the API response look like during migrate? What gets deleted in contract?</p><p><strong>ISO 4217</strong> is the international standard that defines three-letter codes for currencies: <code>USD</code> for the US dollar, <code>EUR</code> for the euro, <code>GBP</code> for the pound sterling, <code>JPY</code> for the Japanese yen. Using the standard instead of a free <code>string</code> guarantees interoperability with any payments API or formatting library. The <code>CurrencyCode</code> value object encapsulates this validation: if the code is not exactly three characters, the object cannot be constructed.</p><p><code>Intl.NumberFormat</code> is the native JavaScript API for formatting numbers according to the regional conventions of a locale. With <code>{ style: 'currency', currency: 'EUR' }</code> and locale <code>es-ES</code>, it formats <code>89.99</code> as <code>89,99 &#8364;</code>. With the same value and locale <code>en-IE</code>, it produces <code>&#8364;89.99</code>. With <code>1000</code> and <code>{ currency: 'JPY' }</code> in <code>ja-JP</code>, it produces <code>&#165;1,000</code>. The formatting is handled by the browser and requires no third-party library.</p><h3>Expand: Migration 001 and updated DTO</h3><p>Add the currency column with a default that makes existing rows immediately valid:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;48338834-7260-4ba3-936e-1501d9e1d354&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[Migration(20240301001, "Expand: add currency_code to products")]
public sealed class AddCurrencyCodeToProducts : Migration
{
    private const string ProductsTable = "products";
    private const string CurrencyCodeColumn = "currency_code";
    private const string DefaultCurrencyCode = "USD";
    private const int IsoCurrencyCodeLength = 3;

    public override void Up()
    {
        Alter.Table(ProductsTable)
            .AddColumn(CurrencyCodeColumn)
            .AsString(IsoCurrencyCodeLength)
            .NotNullable()
            .WithDefaultValue(DefaultCurrencyCode);
    }

    public override void Down()
    {
        Delete.Column(CurrencyCodeColumn).FromTable(ProductsTable);
    }
}</code></pre></div><p>The domain model:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;3b838d59-f678-44ab-9622-a450939cf8aa&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public record CurrencyCode
{
    private const int IsoCurrencyCodeLength = 3;

    public string Value { get; }

    public CurrencyCode(string value)
    {
        if (string.IsNullOrWhiteSpace(value) || value.Length != IsoCurrencyCodeLength)
            throw new ArgumentException(
                $"Currency code must be exactly {IsoCurrencyCodeLength} characters.",
                nameof(value));

        Value = value.ToUpperInvariant();
    }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;2378ed83-9cca-44ac-afb6-c4d2ff5e37d7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class Money
{
    public decimal Amount { get; }
    public CurrencyCode Currency { get; }

    public Money(decimal amount, CurrencyCode currency)
    {
        if (amount &lt; 0)
            throw new ArgumentException("Money amount must not be negative.", nameof(amount));

        Amount = amount;
        Currency = currency;
    }
}</code></pre></div><p>The response DTO carries both the old and new shapes simultaneously. Old consumers read <code>Price</code>. New consumers read <code>Amount</code> and <code>CurrencyCode</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;42bc7ecd-24c7-468e-93b4-5f70b456e5df&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class ProductResponse
{
    public int ProductId { get; init; }
    public string Name { get; init; } = string.Empty;
    public decimal Price { get; init; }
    public decimal Amount { get; init; }
    public string CurrencyCode { get; init; } = string.Empty;

    public static ProductResponse FromDomain(Product product) =&gt;
        new()
        {
            ProductId = product.Identifier.Value,
            Name = product.Name.Value,
            Price = product.Price.Amount,
            Amount = product.Price.Amount,
            CurrencyCode = product.Price.Currency.Value
        };
}</code></pre></div><h3>Migrate</h3><p>The frontend flag and the currency formatter composable:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;dc0fb0f1-375c-4fca-94b9-6f437265205f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import { createFeatureFlagKey } from '@/featureFlags/FeatureFlagKey'

export const ProductFeatureFlags = {
  useCurrencyAwarePrice: createFeatureFlagKey('product.use-currency-aware-price')
} as const</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;492e1995-1288-435e-99cb-5504fcac5875&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">export function useCurrencyFormatter() {
  function formatAmountWithCurrency(
    amount: number,
    currencyCode: string,
    locale: string = navigator.language
  ): string {
    return new Intl.NumberFormat(locale, {
      style: 'currency',
      currency: currencyCode
    }).format(amount)
  }

  return { formatAmountWithCurrency }
}</code></pre></div><p>The product card composable bridges both shapes behind the flag:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;0fd64839-1b28-4f77-b3a4-e47d67437d40&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">import { computed, ref, onMounted } from 'vue'
import { useFeatureFlags } from '@/featureFlags/useFeatureFlags'
import { ProductFeatureFlags } from '@/featureFlags/productFeatureFlags'
import { useCurrencyFormatter } from '@/composables/useCurrencyFormatter'

interface ProductData {
  productId: number
  name: string
  price: number
  amount: number
  currencyCode: string
}

export function useProductCard(product: ProductData) {
  const { isFlagEnabled } = useFeatureFlags()
  const { formatAmountWithCurrency } = useCurrencyFormatter()
  const currencyAwarePriceEnabled = ref(false)

  onMounted(async () =&gt; {
    currencyAwarePriceEnabled.value = await isFlagEnabled(
      ProductFeatureFlags.useCurrencyAwarePrice
    )
  })

  const displayedPrice = computed(() =&gt; {
    if (currencyAwarePriceEnabled.value) {
      return formatAmountWithCurrency(product.amount, product.currencyCode)
    }

    return `$ ${product.price.toFixed(2)}`
  })

  return { displayedPrice }
}</code></pre></div><p>Enable the flag for a subset of users. Validate formatting per locale: <code>es-ES</code>, <code>en-IE</code>, <code>ja-JP</code>. Widen the rollout after each locale validates correctly.</p><h3>Contract</h3><p>Remove <code>Price</code> from the backend DTO. Remove <code>price</code> from the TypeScript type. Remove the flag branch from the composable. The composable has one path:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;1f270b4d-4adb-4215-86a3-1ac600ad7ef3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class ProductResponse
{
    public int ProductId { get; init; }
    public string Name { get; init; } = string.Empty;
    public decimal Amount { get; init; }
    public string CurrencyCode { get; init; } = string.Empty;

    public static ProductResponse FromDomain(Product product) =&gt;
        new()
        {
            ProductId = product.Identifier.Value,
            Name = product.Name.Value,
            Amount = product.Price.Amount,
            CurrencyCode = product.Price.Currency.Value
        };
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;typescript&quot;,&quot;nodeId&quot;:&quot;c932cfae-956e-4ebe-8374-ac45b80ebe52&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-typescript">export function useProductCard(product: ProductData) {
  const { formatAmountWithCurrency } = useCurrencyFormatter()

  const displayedPrice = computed(() =&gt;
    formatAmountWithCurrency(product.amount, product.currencyCode)
  )

  return { displayedPrice }
}</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8G2E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F067357c3-2941-48ee-a4d6-db1661701644_1860x1676.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8G2E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F067357c3-2941-48ee-a4d6-db1661701644_1860x1676.png 424w, https://substackcdn.com/image/fetch/$s_!8G2E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F067357c3-2941-48ee-a4d6-db1661701644_1860x1676.png 848w, https://substackcdn.com/image/fetch/$s_!8G2E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F067357c3-2941-48ee-a4d6-db1661701644_1860x1676.png 1272w, https://substackcdn.com/image/fetch/$s_!8G2E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F067357c3-2941-48ee-a4d6-db1661701644_1860x1676.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8G2E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F067357c3-2941-48ee-a4d6-db1661701644_1860x1676.png" width="1456" height="1312" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/067357c3-2941-48ee-a4d6-db1661701644_1860x1676.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1312,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:169831,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F067357c3-2941-48ee-a4d6-db1661701644_1860x1676.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_!8G2E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F067357c3-2941-48ee-a4d6-db1661701644_1860x1676.png 424w, https://substackcdn.com/image/fetch/$s_!8G2E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F067357c3-2941-48ee-a4d6-db1661701644_1860x1676.png 848w, https://substackcdn.com/image/fetch/$s_!8G2E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F067357c3-2941-48ee-a4d6-db1661701644_1860x1676.png 1272w, https://substackcdn.com/image/fetch/$s_!8G2E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F067357c3-2941-48ee-a4d6-db1661701644_1860x1676.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>The migration is complete only when both the old shape and the old behaviour have disappeared from every layer.</p><h2>Example 6: replacing an infrastructure dependency with Branch by Abstraction</h2><p>Your system sends transactional emails through a direct dependency on <code>SmtpEmailSender</code>. The SMTP provider is being retired. You need to replace it with <code>SendGridEmailSender</code>. The system sends emails continuously. You cannot stop it to do the swap, and you cannot afford to find out at midnight that the new provider behaves differently in production than it did in staging.</p><p>A direct replacement would mean changing every class that depends on <code>SmtpEmailSender</code> and deploying with no safety net. If the new provider has a configuration problem or a quota behaviour you did not expect, the rollback is a full redeployment.</p><p>Branch by Abstraction solves this differently. The suture point is an interface introduced between the callers and the implementation. That interface exists before the new implementation does. Once it exists, the old and new implementations can coexist behind it, and a feature flag controls which one is active. Rollback is disabling a flag, not reverting a deployment.</p><p>This is the key distinction from Example 1 (delegation): in delegation, you change the shape of a single member and keep callers working via a forwarding path. In Branch by Abstraction, you replace an entire dependency that callers couple to directly, and the abstraction is the stable surface they couple to instead.</p><h3>Before</h3><p>The notification service depends directly on the concrete implementation:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;cd6f933a-6f35-495b-9102-5dae73f7a044&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class NotificationService
{
    private readonly SmtpEmailSender _emailSender;

    public NotificationService(SmtpEmailSender emailSender)
    {
        _emailSender = emailSender;
    }

    public async Task SendOrderConfirmationAsync(Order order, EmailAddress recipient)
    {
        var subject = new EmailSubject($"Order {order.Identifier.Value} confirmed");
        var body = new EmailBody(BuildOrderConfirmationBody(order));
        await _emailSender.SendAsync(recipient, subject, body);
    }

    private static string BuildOrderConfirmationBody(Order order) =&gt;
        $"Your order {order.Identifier.Value} for {order.Total.Amount} {order.Total.Currency.Value} has been confirmed.";
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;c90d79ac-09cf-40fe-a8b1-0e63102b2774&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class SmtpEmailSender
{
    private readonly SmtpConfiguration _configuration;

    public SmtpEmailSender(SmtpConfiguration configuration)
    {
        _configuration = configuration;
    }

    public async Task SendAsync(EmailAddress recipient, EmailSubject subject, EmailBody body)
    {
        using var client = new SmtpClient(_configuration.Host, _configuration.Port);
        var message = new MailMessage(_configuration.SenderAddress, recipient.Value)
        {
            Subject = subject.Value,
            Body = body.Value
        };
        await client.SendMailAsync(message);
    }
}</code></pre></div><h3>Expand</h3><p>Introduce the port. Make <code>SmtpEmailSender</code> implement it. Change <code>NotificationService</code> to depend on the port instead of the concrete class. Deploy. Nothing changes behaviourally. The abstraction now exists as a stable surface:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;e7106ff0-7135-42e6-a242-8144ca205101&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public interface EmailSender
{
    Task SendAsync(EmailAddress recipient, EmailSubject subject, EmailBody body);
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;7f00cd08-b2a9-4d2b-9182-94be5c3d7c29&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class SmtpEmailSender : EmailSender
{
    private readonly SmtpConfiguration _configuration;

    public SmtpEmailSender(SmtpConfiguration configuration)
    {
        _configuration = configuration;
    }

    public async Task SendAsync(EmailAddress recipient, EmailSubject subject, EmailBody body)
    {
        using var client = new SmtpClient(_configuration.Host, _configuration.Port);
        var message = new MailMessage(_configuration.SenderAddress, recipient.Value)
        {
            Subject = subject.Value,
            Body = body.Value
        };
        await client.SendMailAsync(message);
    }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;4214f55b-1756-482a-9eca-c6026d4fa193&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class NotificationService
{
    private readonly EmailSender _emailSender;

    public NotificationService(EmailSender emailSender)
    {
        _emailSender = emailSender;
    }

    public async Task SendOrderConfirmationAsync(Order order, EmailAddress recipient)
    {
        var subject = new EmailSubject($"Order {order.Identifier.Value} confirmed");
        var body = new EmailBody(BuildOrderConfirmationBody(order));
        await _emailSender.SendAsync(recipient, subject, body);
    }

    private static string BuildOrderConfirmationBody(Order order) =&gt;
        $"Your order {order.Identifier.Value} for {order.Total.Amount} {order.Total.Currency.Value} has been confirmed.";
}</code></pre></div><h3>Migrate</h3><p>Build the new implementation. Then introduce a routing adapter that uses the feature flag to decide which implementation to call. The flag controls which provider handles traffic. <code>NotificationService</code> is unaware of any of this: it still calls <code>EmailSender.SendAsync</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;c03bbc34-692a-4b60-9017-99d280b38dfa&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class SendGridEmailSender : EmailSender
{
    private readonly SendGridConfiguration _configuration;
    private readonly SendGridClient _client;

    public SendGridEmailSender(SendGridConfiguration configuration)
    {
        _configuration = configuration;
        _client = new SendGridClient(_configuration.ApiKey);
    }

    public async Task SendAsync(EmailAddress recipient, EmailSubject subject, EmailBody body)
    {
        var message = MailHelper.CreateSingleEmail(
            from: new EmailAddress(_configuration.SenderAddress),
            to: new EmailAddress(recipient.Value),
            subject: subject.Value,
            plainTextContent: body.Value,
            htmlContent: null);

        await _client.SendEmailAsync(message);
    }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;0d03d0a1-88ae-4567-949f-161265463158&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public static class EmailFeatureFlagKeys
{
    public static readonly FeatureFlagKey UseSendGrid =
        new FeatureFlagKey("email.use-sendgrid-sender");
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;add4f558-c159-43f0-a1d9-a917a9135fa9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class FeatureFlagRoutingEmailSender : EmailSender
{
    private readonly SmtpEmailSender _smtpSender;
    private readonly SendGridEmailSender _sendGridSender;
    private readonly FeatureFlagService _featureFlagService;

    public FeatureFlagRoutingEmailSender(
        SmtpEmailSender smtpSender,
        SendGridEmailSender sendGridSender,
        FeatureFlagService featureFlagService)
    {
        _smtpSender = smtpSender;
        _sendGridSender = sendGridSender;
        _featureFlagService = featureFlagService;
    }

    public async Task SendAsync(EmailAddress recipient, EmailSubject subject, EmailBody body)
    {
        var useSendGrid = await _featureFlagService.IsEnabledAsync(EmailFeatureFlagKeys.UseSendGrid);

        var sender = useSendGrid
            ? (EmailSender)_sendGridSender
            : _smtpSender;

        await sender.SendAsync(recipient, subject, body);
    }
}</code></pre></div><p>Registration wires it all together:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;4eb38e41-5508-4802-8a3f-5f516f8b2a00&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">builder.Services.AddSingleton&lt;SmtpEmailSender&gt;();
builder.Services.AddSingleton&lt;SendGridEmailSender&gt;();
builder.Services.AddScoped&lt;EmailSender, FeatureFlagRoutingEmailSender&gt;();</code></pre></div><p>Enable the flag for a small percentage of traffic. Monitor delivery rates, bounce rates, and latency for both providers in parallel. Widen the rollout when the new provider proves reliable. The old provider remains available as a fallback for as long as you need it.</p><h3>Contract</h3><p>Once <code>SendGridEmailSender</code> has proved itself in production and the flag is fully enabled for all traffic, delete <code>SmtpEmailSender</code>, delete <code>FeatureFlagRoutingEmailSender</code>, remove the flag check, and register <code>SendGridEmailSender</code> directly as <code>EmailSender</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;2388e5b3-f315-4f27-b507-8d02392ae27c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">builder.Services.AddScoped&lt;EmailSender, SendGridEmailSender&gt;();</code></pre></div><p><code>NotificationService</code> has not changed since the expand phase. It never needed to know that the provider changed.</p><p>The lesson is the point of the suture. Branch by Abstraction does not route traffic between two systems. It places a seam inside the code, between a caller and a dependency, and routes execution between two implementations of the same interface. The caller never moves. The implementations are what change.</p><h2>Example 7: replacing a legacy module with Strangler Fig</h2><p>Your system has a <code>ReportsModule</code> inside the monolith. It generates business reports by running complex SQL queries directly against the production database. It works, but it is hard to test, impossible to scale independently, and its SQL has grown to a point where nobody is confident changing it. A new <code>ReportingService</code>, a separate deployable, is being built to replace it.</p><p>You cannot do a cutover rewrite. The new service is not finished yet. Some report types will be migrated before others. Business users depend on reports daily. If the new service has a problem, you need to fall back in seconds, not in the time it takes to redeploy the monolith.</p><p>Strangler Fig is the pattern for this situation. Its name comes from the strangler fig tree, which grows around a host tree and gradually takes its place. The old module is not removed first and replaced: it remains alive and continues serving traffic while the new service grows alongside it. Traffic is rerouted incrementally, one report type at a time. The old module is deleted only when it has no remaining consumers.</p><p>The key distinction from Branch by Abstraction is where the suture point lives. In Branch by Abstraction, the suture is inside the code: an interface between a caller and a dependency. In Strangler Fig, the suture is in the routing layer: a proxy, middleware, or API gateway that decides where each request goes. The callers do not change. The routing logic changes.</p><h3>Before</h3><p>The monolith handles all report requests directly:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;ada175da-f2cb-4813-8f03-d09cf575f535&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[ApiController]
[Route("api/reports")]
public sealed class ReportsController : ControllerBase
{
    private readonly LegacyReportQueryService _queryService;

    public ReportsController(LegacyReportQueryService queryService)
    {
        _queryService = queryService;
    }

    [HttpGet("sales-summary")]
    public async Task&lt;IActionResult&gt; GetSalesSummary([FromQuery] DateRange range)
    {
        var report = await _queryService.BuildSalesSummaryAsync(range);
        return Ok(report);
    }

    [HttpGet("inventory-status")]
    public async Task&lt;IActionResult&gt; GetInventoryStatus()
    {
        var report = await _queryService.BuildInventoryStatusAsync();
        return Ok(report);
    }

    [HttpGet("customer-retention")]
    public async Task&lt;IActionResult&gt; GetCustomerRetention([FromQuery] DateRange range)
    {
        var report = await _queryService.BuildCustomerRetentionAsync(range);
        return Ok(report);
    }
}</code></pre></div><h3>Expand</h3><p>Introduce the routing middleware. At this point it forwards all requests to the legacy module unchanged. The new service does not exist yet. Nothing changes in observable behaviour. But the routing layer is now in place, and adding a new route to the new service will not require touching <code>ReportsController</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;f6fed946-a0fb-45e5-9a48-7374a4aeabe0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public static class ReportRouteKeys
{
    public static readonly FeatureFlagKey SalesSummaryUsesNewService =
        new FeatureFlagKey("reports.sales-summary-uses-new-service");

    public static readonly FeatureFlagKey InventoryStatusUsesNewService =
        new FeatureFlagKey("reports.inventory-status-uses-new-service");

    public static readonly FeatureFlagKey CustomerRetentionUsesNewService =
        new FeatureFlagKey("reports.customer-retention-uses-new-service");
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;5cd022f8-a23c-4e3b-800c-2d9fc86ec391&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">public sealed class ReportRoutingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly FeatureFlagService _featureFlagService;
    private const string NewReportingServiceBaseUrl = "http://reporting-service";

    public ReportRoutingMiddleware(
        RequestDelegate next,
        IHttpClientFactory httpClientFactory,
        FeatureFlagService featureFlagService)
    {
        _next = next;
        _httpClientFactory = httpClientFactory;
        _featureFlagService = featureFlagService;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var flagKey = ResolveFlag(context.Request.Path);

        if (flagKey is null)
        {
            await _next(context);
            return;
        }

        var routeToNewService = await _featureFlagService.IsEnabledAsync(flagKey);

        if (!routeToNewService)
        {
            await _next(context);
            return;
        }

        await ForwardToNewServiceAsync(context);
    }

    private static FeatureFlagKey? ResolveFlag(PathString path) =&gt;
        path.Value switch
        {
            var p when p!.Contains("sales-summary") =&gt; ReportRouteKeys.SalesSummaryUsesNewService,
            var p when p!.Contains("inventory-status") =&gt; ReportRouteKeys.InventoryStatusUsesNewService,
            var p when p!.Contains("customer-retention") =&gt; ReportRouteKeys.CustomerRetentionUsesNewService,
            _ =&gt; null
        };

    private async Task ForwardToNewServiceAsync(HttpContext context)
    {
        var client = _httpClientFactory.CreateClient();
        var targetUrl = $"{NewReportingServiceBaseUrl}{context.Request.Path}{context.Request.QueryString}";

        using var forwardRequest = new HttpRequestMessage(
            new HttpMethod(context.Request.Method),
            targetUrl);

        var response = await client.SendAsync(forwardRequest);
        context.Response.StatusCode = (int)response.StatusCode;

        var content = await response.Content.ReadAsStringAsync();
        await context.Response.WriteAsync(content);
    }
}</code></pre></div><p>Registration:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;281f345f-bc4e-46b3-a299-66ce2dfc7ee7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">app.UseMiddleware&lt;ReportRoutingMiddleware&gt;();</code></pre></div><h3>Migrate</h3><p>The new <code>ReportingService</code> is built and deployed as a separate process. Report types migrate one at a time. When <code>reports.sales-summary-uses-new-service</code> is enabled in ConfigCat, all requests for the sales summary report route to the new service. The legacy <code>ReportsController</code> still handles inventory and retention. Both are live simultaneously. Neither knows about the other.</p><p>The new service exposes the same API paths so callers do not change:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;c72024fe-0584-4715-b471-916a0c8b23a7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">[ApiController]
[Route("api/reports")]
public sealed class ReportingController : ControllerBase
{
    private readonly SalesSummaryQueryHandler _salesSummaryHandler;
    private readonly InventoryStatusQueryHandler _inventoryStatusHandler;
    private readonly CustomerRetentionQueryHandler _customerRetentionHandler;

    public ReportingController(
        SalesSummaryQueryHandler salesSummaryHandler,
        InventoryStatusQueryHandler inventoryStatusHandler,
        CustomerRetentionQueryHandler customerRetentionHandler)
    {
        _salesSummaryHandler = salesSummaryHandler;
        _inventoryStatusHandler = inventoryStatusHandler;
        _customerRetentionHandler = customerRetentionHandler;
    }

    [HttpGet("sales-summary")]
    public async Task&lt;IActionResult&gt; GetSalesSummary([FromQuery] DateRange range)
    {
        var report = await _salesSummaryHandler.HandleAsync(range);
        return Ok(report);
    }

    [HttpGet("inventory-status")]
    public async Task&lt;IActionResult&gt; GetInventoryStatus()
    {
        var report = await _inventoryStatusHandler.HandleAsync();
        return Ok(report);
    }

    [HttpGet("customer-retention")]
    public async Task&lt;IActionResult&gt; GetCustomerRetention([FromQuery] DateRange range)
    {
        var report = await _customerRetentionHandler.HandleAsync(range);
        return Ok(report);
    }
}</code></pre></div><p>Enable each flag independently as the corresponding report type is ready and validated. The strangler fig grows one branch at a time.</p><h3>Contract</h3><p>Once all three flags are permanently enabled and no traffic reaches the legacy module, delete <code>ReportsController</code>, delete <code>LegacyReportQueryService</code>, and delete <code>ReportRoutingMiddleware</code>. Remove the three flags from ConfigCat. The new service handles all report traffic directly, with no routing layer in the middle.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a6mZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4639d588-e8d2-4c0e-bfb3-3af37a826830_2018x1692.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a6mZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4639d588-e8d2-4c0e-bfb3-3af37a826830_2018x1692.png 424w, https://substackcdn.com/image/fetch/$s_!a6mZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4639d588-e8d2-4c0e-bfb3-3af37a826830_2018x1692.png 848w, https://substackcdn.com/image/fetch/$s_!a6mZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4639d588-e8d2-4c0e-bfb3-3af37a826830_2018x1692.png 1272w, https://substackcdn.com/image/fetch/$s_!a6mZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4639d588-e8d2-4c0e-bfb3-3af37a826830_2018x1692.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!a6mZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4639d588-e8d2-4c0e-bfb3-3af37a826830_2018x1692.png" width="1456" height="1221" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4639d588-e8d2-4c0e-bfb3-3af37a826830_2018x1692.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1221,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:189303,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4639d588-e8d2-4c0e-bfb3-3af37a826830_2018x1692.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_!a6mZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4639d588-e8d2-4c0e-bfb3-3af37a826830_2018x1692.png 424w, https://substackcdn.com/image/fetch/$s_!a6mZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4639d588-e8d2-4c0e-bfb3-3af37a826830_2018x1692.png 848w, https://substackcdn.com/image/fetch/$s_!a6mZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4639d588-e8d2-4c0e-bfb3-3af37a826830_2018x1692.png 1272w, https://substackcdn.com/image/fetch/$s_!a6mZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4639d588-e8d2-4c0e-bfb3-3af37a826830_2018x1692.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The difference from Branch by Abstraction is visible in this diagram. In Branch by Abstraction, the routing decision happens inside a single process, between an interface and two implementations, and callers see one coherent dependency. In Strangler Fig, the routing decision happens at the network layer, between two separate deployables, and callers see one coherent API. In both cases, the old and new implementations coexist safely. In both cases, the migration is gradual. What differs is the granularity and the location of the seam.</p><p>Strangler Fig carries a specific risk that Branch by Abstraction does not: the routing middleware becomes load-bearing infrastructure. If the middleware has a bug, all reports break. If it has a memory leak, it affects the monolith. The routing layer should be thin, observable, and tested. Its only responsibility is to decide where to send a request. All business logic stays in the implementations on either side of it.</p><h2>Pitfalls</h2><p>Parallel Change sounds tidy when described in three words. In practice, teams usually fail not because they misunderstand the pattern, but because they mishandle the temporary state it creates.</p><p>The first failure mode is the <strong>contract step that never ships</strong>. The visible business benefit arrives during Expand or Migrate. Contract rarely has a direct user-facing value, so it loses the prioritisation argument unless someone owns it from the beginning. The fix is procedural, not technical: open the contract ticket when you open the expand change. Assign it. Give it a date derived from something real, like the mobile team&#8217;s migration timeline or a deployment cadence. The contract step is not optional follow-up. It is the second half of the same decision.</p><p>The second failure mode is <strong>crossing the point of no easy return without naming it</strong>. Teams tell themselves they can &#8220;always flip it back&#8221; long after the old path has stopped receiving fresh data. At that point the rollback story has already changed, whether they noticed or not. The fix is to name the point explicitly before the migration starts. In a database migration, write it into the migration comments. In a flag-based migration, note it in the flag description in ConfigCat. The knowledge of where rollback stops being free should be shared with everyone who might be asked to roll back at two in the morning.</p><p>The third failure mode is <strong>feature flags that outlive the migration</strong>. A migration flag that survives long after the migration is complete is no longer making release safer. It is just another branch that future engineers must understand, preserve, and account for in every subsequent change. The fix is to create the removal ticket when the flag is created, exactly as with the contract ticket, and to treat the flag&#8217;s existence as a temporary cost rather than a default state.</p><p>The fourth failure mode is <strong>two valid shapes confusing new work</strong>. If old and new coexist without any marking that one is transitional, new code keeps getting written against the old path. The <code>[Obsolete]</code> attribute on C# members, deprecation annotations in OpenAPI documentation, and a visible note in the expand change all prevent the silent accumulation of new dependencies on something that is scheduled to disappear.</p><h2>When to use Parallel Change</h2><p>Not every change needs this discipline. If you control all consumers and can update them safely in one atomic change, if the system is not live in a way that creates compatibility risk, and if rollback would be trivial, a direct change is usually better. The pattern adds intermediate steps. Intermediate steps have a cost.</p><p>Parallel Change becomes the right tool when direct coordination is unrealistic, risky, or expensive. Uncontrolled consumers, always-on traffic, shared schemas, independent deployment cycles, public contracts, or a legacy system that would otherwise demand a high-risk cutover are all situations where the cost of intermediate steps is lower than the cost of the alternative. The last two categories map directly to Branch by Abstraction (Example 6) and Strangler Fig (Example 7).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!J2xU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875c1839-6762-4b67-9874-d2e8cb1cc5a6_1340x2967.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!J2xU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875c1839-6762-4b67-9874-d2e8cb1cc5a6_1340x2967.png 424w, https://substackcdn.com/image/fetch/$s_!J2xU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875c1839-6762-4b67-9874-d2e8cb1cc5a6_1340x2967.png 848w, https://substackcdn.com/image/fetch/$s_!J2xU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875c1839-6762-4b67-9874-d2e8cb1cc5a6_1340x2967.png 1272w, https://substackcdn.com/image/fetch/$s_!J2xU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875c1839-6762-4b67-9874-d2e8cb1cc5a6_1340x2967.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!J2xU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875c1839-6762-4b67-9874-d2e8cb1cc5a6_1340x2967.png" width="254" height="562.4014925373134" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/875c1839-6762-4b67-9874-d2e8cb1cc5a6_1340x2967.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2967,&quot;width&quot;:1340,&quot;resizeWidth&quot;:254,&quot;bytes&quot;:228417,&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://emmanuelvalverderamos.substack.com/i/190770945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875c1839-6762-4b67-9874-d2e8cb1cc5a6_1340x2967.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_!J2xU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875c1839-6762-4b67-9874-d2e8cb1cc5a6_1340x2967.png 424w, https://substackcdn.com/image/fetch/$s_!J2xU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875c1839-6762-4b67-9874-d2e8cb1cc5a6_1340x2967.png 848w, https://substackcdn.com/image/fetch/$s_!J2xU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875c1839-6762-4b67-9874-d2e8cb1cc5a6_1340x2967.png 1272w, https://substackcdn.com/image/fetch/$s_!J2xU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875c1839-6762-4b67-9874-d2e8cb1cc5a6_1340x2967.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Conclusions</h2><p>The fear that keeps these changes stuck in the backlog is usually not irrational. It comes from experience. Engineers have seen systems break when too many moving parts had to shift together. They have seen &#8220;small&#8221; changes trigger incidents because the real problem was never the code in isolation. It was the coordination burden hiding around it.</p><p>Parallel Change does not remove risk by pretending the system is simpler than it is. It reduces risk by respecting reality. Different consumers move at different speeds. Old and new shapes often need to coexist for a while. Data survives deployments. Rollback is only easy up to a point. These are not exceptions. They are normal properties of live systems.</p><p>Once that is accepted, the change stops looking like one dangerous moment and starts looking like a sequence of safe states. A team that thinks this way can split a live database column without downtime, introduce a new API contract while a mobile app finishes its own cycle, refactor a constructor used across many call sites without freezing the codebase, and simplify a builder hierarchy that was compensating for a domain misunderstanding, doing so in a way that gave the team space to find the better design.</p><p>The part that requires the most discipline is not Expand. Teams usually find it satisfying to add the new path. It is not Migrate either. That phase often has visible momentum. The hard part is Contract, because once the new behaviour is live, it is easy to pretend the work is done. It is not. Contract is how the system returns to clarity. It is how temporary compatibility becomes temporary in truth rather than only in intention. It is how the next change stays possible.</p><p>A system stays healthy not because it avoids change, but because it learns how to change without forcing everything to move together.</p><h2>References</h2><ul><li><p>Danilo Sato. <em>Parallel Change</em>. <a href="https://martinfowler.com/bliki/ParallelChange.html">https://martinfowler.com/bliki/ParallelChange.html</a></p></li><li><p>Tim Wellhausen. <em>Expand and Contract</em>. <a href="https://martinfowler.com/articles/patterns-of-distributed-systems/two-phase-commit.html">https://martinfowler.com/articles/patterns-of-distributed-systems/two-phase-commit.html</a></p></li><li><p>Martin Fowler. <em>Branch by Abstraction</em>. <a href="https://martinfowler.com/bliki/BranchByAbstraction.html">https://martinfowler.com/bliki/BranchByAbstraction.html</a></p></li><li><p>Martin Fowler. <em>Strangler Fig Application</em>. <a href="https://martinfowler.com/bliki/StranglerFigApplication.html">https://martinfowler.com/bliki/StranglerFigApplication.html</a></p></li><li><p>Martin Fowler. <em>Canary Release</em>. <a href="https://martinfowler.com/bliki/CanaryRelease.html">https://martinfowler.com/bliki/CanaryRelease.html</a></p></li><li><p>Pete Hodgson. <em>Feature Toggles (aka Feature Flags)</em>. <a href="https://martinfowler.com/articles/feature-toggles.html">https://martinfowler.com/articles/feature-toggles.html</a></p></li><li><p>Martin Fowler. <em>Continuous Integration</em>. <a href="https://martinfowler.com/articles/continuousIntegration.html">https://martinfowler.com/articles/continuousIntegration.html</a></p></li><li><p>FluentMigrator. <em>FluentMigrator documentation</em>. </p><p><a href="https://fluentmigrator.github.io">https://fluentmigrator.github.io</a></p></li><li><p>ConfigCat. <em>.NET SDK Reference</em>. <a href="https://configcat.com/docs/sdk-reference/dotnet">https://configcat.com/docs/sdk-reference/dotnet</a></p></li><li><p>ConfigCat. <em>Polling Modes and Caching</em>. <a href="https://configcat.com/docs/advanced/caching">https://configcat.com/docs/advanced/caching</a></p></li><li><p>Vue.js. <em>Composables</em>. <a href="https://vuejs.org/guide/reusability/composables.html">https://vuejs.org/guide/reusability/composables.html</a></p></li><li><p>Manuel Rivero and the Codesai team (2025). <em>Parallel change example: simplifying some test builders</em>. <a href="https://codesai.com/posts/2025/11/simplifying-builders-parallel-change">https://codesai.com/posts/2025/11/simplifying-builders-parallel-change</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Pain = Coupling × Volatility × Distance]]></title><description><![CDATA[Why software change becomes expensive, and why the real problem is often socio-technical before it is purely technical]]></description><link>https://emmanuelvalverderamos.substack.com/p/pain-coupling-volatility-distance</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/pain-coupling-volatility-distance</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 02 Jun 2026 05:00:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hp9G!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Introduction</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hp9G!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hp9G!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!hp9G!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!hp9G!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!hp9G!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hp9G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6877672,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/189802324?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.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_!hp9G!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!hp9G!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!hp9G!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!hp9G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0e9e383-50c5-4259-b76a-6b176a9b1217_2816x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A change request arrives.</p><p>On paper, it looks small. Add a field. Adjust a rule. Move a validation. Rename a concept. Change the order of a workflow. Nobody reading the ticket would call it a major initiative.</p><p>And yet the change does not stay small.</p><p>A team touches the API. Another team owns the data model. Someone else owns the deployment pipeline. Security needs to review it. Product wants to revisit the rollout. Operations has concerns about observability. A senior engineer becomes a bottleneck because only they understand the edge cases. The work starts as a minor request and ends as a negotiation across code, calendars, ownership boundaries, and competing priorities.</p><p>This is one of the most common and least honestly described problems in software delivery. The issue is rarely just that code is messy. The issue is that change has to travel too far through a system that is both structurally entangled and organizationally fragmented.</p><p>That is why this formula is useful:</p><div class="callout-block" data-callout="true"><p style="text-align: center;"><strong>Pain = Coupling &#215; Volatility &#215; Distance</strong></p></div><p>There is another way to read the same idea:</p><div class="callout-block" data-callout="true"><p style="text-align: center;"><strong>Pain = high coupling &#215; high rate of change &#215; low cohesion</strong></p></div><p>This is not meant as a literal law of nature or a mathematically exact model. It is a practical lens. Its value is that it captures, in one line, something many teams feel every day but struggle to explain clearly: most software pain does not come from change itself. It comes from how far change must propagate through a system whose boundaries do not match the real shape of the work.</p><p>Once you see that, a great deal becomes clearer. Refactoring stops looking like code cleanup and starts looking like economic work. Team topology stops looking like org chart design and starts looking like part of the architecture. Coupling and cohesion stop being abstract textbook terms and become predictors of delivery pain. Lean stops being merely about efficiency and becomes a way to reason about the cost of movement through a system. Extreme Programming stops being a set of engineering rituals and reveals itself as a disciplined attempt to reduce the distance between intent, implementation, feedback, and delivery.</p><p>That is the argument of this article. Software pain is usually compounded by three forces at once. Coupling spreads change. Volatility determines how often you must pay for that spread. Distance makes every propagation more expensive because each boundary crossed introduces delay, coordination, translation, waiting, and risk.</p><p>If you want cheaper software change, you do not only need better code. You need shorter paths of change.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>The real unit of cost is not the feature</h2><p><strong>A lot of engineering conversations still assume that the main question is how much a feature costs to build</strong>. That is understandable, but it is incomplete. The more important question is usually this: what kind of future changes does this design make cheap, and what kind does it make expensive?</p><p>Software is not a static artifact. It is a changing one. It is created once, but modified constantly. Its real cost emerges over time, through extension, correction, adaptation, integration, migration, support, and reinterpretation. That means the economics of software are mostly the economics of change.</p><p>This point matters because it immediately changes what counts as a good design decision. A design is not good simply because it was fast to implement. <strong>It is good if it keeps the likely path of future change local, comprehensible, and inexpensive</strong>.</p><p>A shortcut in a stable area may remain tolerable for years. The same shortcut in a highly volatile area becomes a recurring tax. Every small request reopens the same dependency chain. Every adjustment forces the same conversations. Every release repeats the same structural penalty.</p><p>That is why pain needs to be understood as the cost of future modification, not just the discomfort of present implementation.</p><h2>Why the formula works</h2><p>The formula works because each part captures a different dimension of change cost.</p><p><strong>Coupling</strong> tells you how much a change in one place forces a change in another.</p><p><strong>Volatility</strong> tells you how often that area of the system is likely to change.</p><p><strong>Distance</strong> tells you how many boundaries the change must cross, and how costly those crossings are.</p><p>If coupling is low, many changes stay local.</p><p>If volatility is low, even a bad design may not hurt often.</p><p>If distance is short, coordination remains manageable.</p><p>But when all three are high, the pain compounds. Not additively, but multiplicatively. A tightly coupled change in a frequently changing area is already expensive. If that same change must also cross several code boundaries, team boundaries, approval boundaries, or semantic boundaries, the cost grows nonlinearly.</p><p>That is why some systems feel disproportionately painful. The codebase alone does not explain the experience. The organization alone does not explain it either. The pain comes from the interaction between the software structure and the social structure that must move it.</p><h2>Coupling is the first multiplier</h2><p>Coupling is often introduced as a purely technical concept, but it is better understood as a property of dependency. Something is coupled when it cannot move without dragging something else with it.</p><p>At code level, that might mean one module cannot change without changing another. At architectural level, it might mean a service depends on another service&#8217;s internal decisions. At organizational level, it might mean one team cannot finish a task without another team changing their priorities.</p><p>Not all coupling is bad. Systems are made of interdependent parts. The problem is excessive coupling, hidden coupling, accidental coupling, or coupling placed across the wrong boundary.</p><p>High coupling turns local work into distributed work. A change that should have remained contained suddenly radiates outward. What looked like one decision becomes three. One edit becomes seven. One team becomes four. This is where software starts to feel heavy.</p><p>Classic code smells help make this visible. Feature Envy suggests that behavior lives too far from the data or rules it depends on. Shotgun Surgery suggests that a single logical change has been scattered across too many places. Both are signs that the true unit of change does not match the current structure.</p><p>This matters because coupling is not only about complexity. It is about movement. The more tightly coupled the system, the further change must travel to become complete.</p><h2>Cohesion is the inverse story</h2><p>If coupling tells you how much a change spreads, cohesion tells you whether the boundary makes sense.</p><p>A cohesive boundary groups together things that belong together, change together, and make sense together. A cohesive module contains behavior and rules that evolve for similar reasons. A cohesive service owns a capability whose changes mostly remain inside it. A cohesive team can take meaningful work from idea to production without constantly depending on other teams to finish the job.</p><p>Low cohesion means the boundary is lying.</p><p>On paper, responsibilities are grouped one way. In practice, the real path of change follows another. The data lives in one place, the rules live somewhere else, the authority sits with another team, and the operational consequences land elsewhere again. The result is fragmentation disguised as architecture.</p><p>This is why the alternative reading of the formula is so strong:</p><div class="callout-block" data-callout="true"><p style="text-align: center;"><strong>Pain = high coupling &#215; high rate of change &#215; low cohesion</strong></p></div><p>Low cohesion is one of the main reasons distance emerges. If things that should evolve together are artificially split apart, then change must cross the gap. And once that gap exists, the cost is no longer only technical. It becomes social and organizational too.</p><p>This applies far beyond software. Coupling and cohesion are not exclusive to code. We see them in teams, responsibilities, communication patterns, even personal relationships. When two units are formally separate but functionally inseparable, the cost of coordination rises. In software organizations, this is especially dangerous when team boundaries and domain boundaries do not align with the actual flow of change.</p><h2>Volatility tells you how often you repay the design debt</h2><p>Volatility is the rate of change a part of the system is exposed to. Some areas remain stable. Others are under constant pressure from regulation, customer behavior, product learning, market response, operational incidents, pricing changes, workflow adjustments, or integration churn.</p><blockquote><p>In building business software requirements changes are the norm, the question is what we do about it</p><p>- <a href="https://www.martinfowler.com/articles/newMethodologyOriginal.html">Martin Fowler</a></p></blockquote><p></p><blockquote><p>In building business software requirements changes are the norm, the question is what we do about it</p><p>- <a href="https://agilemanifesto.org/principles.html">Agile Manifesto</a></p></blockquote><p>Volatility is not a problem in itself. In many contexts, it is a sign that the business is learning. A stable system that never changes may be calm, but it may also be irrelevant. The real question is not whether a part of the system changes often. The real question is whether the system was designed so that frequent change remains local and affordable.</p><p>This is where software economics becomes concrete. A poor structure in a quiet area may survive because the tax is rarely collected. A poor structure in a volatile area becomes painful immediately because the tax is collected over and over again.</p><p>Volatility tells you how often your structural mistakes become real costs.</p><p>This is why architecture should not be distributed evenly. The most careful design effort belongs where change is likely to concentrate. If an area of the product is strategically volatile, then broad coupling there will be disproportionately expensive. Good engineering judgment means placing flexibility where the business will need movement, not spreading elaborate structure everywhere in advance.</p><h2>Distance is where technical pain becomes organizational pain</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rxv7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67145165-c4c4-4f2b-8614-ba700d69b8b5_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rxv7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67145165-c4c4-4f2b-8614-ba700d69b8b5_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!rxv7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67145165-c4c4-4f2b-8614-ba700d69b8b5_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!rxv7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67145165-c4c4-4f2b-8614-ba700d69b8b5_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!rxv7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67145165-c4c4-4f2b-8614-ba700d69b8b5_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rxv7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67145165-c4c4-4f2b-8614-ba700d69b8b5_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/67145165-c4c4-4f2b-8614-ba700d69b8b5_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3389014,&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://emmanuelvalverderamos.substack.com/i/189802324?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67145165-c4c4-4f2b-8614-ba700d69b8b5_1408x752.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_!rxv7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67145165-c4c4-4f2b-8614-ba700d69b8b5_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!rxv7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67145165-c4c4-4f2b-8614-ba700d69b8b5_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!rxv7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67145165-c4c4-4f2b-8614-ba700d69b8b5_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!rxv7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67145165-c4c4-4f2b-8614-ba700d69b8b5_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Distance is the most underestimated part of the formula, and often the most revealing.</p><p>Distance is not just physical distance. It is not merely whether two people sit in different rooms or work in different time zones. Distance is the number and cost of boundaries a change must cross.</p><blockquote><p>Things that change together should be together</p><p>- <a href="https://martinfowler.com/bliki/GetterEradicator.html">Martin Fowler</a></p></blockquote><p>Those boundaries can be technical. Different services, repositories, pipelines, data stores, runtime environments, or ownership models.</p><p>They can be organizational. Different teams, roadmaps, reporting lines, budgeting structures, approval chains, or support models.</p><p>They can be operational. Different deployment permissions, release gates, access models, observability responsibilities, or incident ownership.</p><p>They can also be semantic. Different domain language, different mental models, different incentives, different notions of what &#8220;done&#8221; means.</p><p>Every boundary crossing adds friction. Someone has to explain context. Someone has to wait. Someone has to switch priorities. Someone has to interpret a request through a different local lens. Even when everybody is competent and collaborative, the cost remains. Coordination is work.</p><blockquote><p>a design effort should be organized according to the need for communication</p><p>- <a href="https://www.melconway.com/research/committees.html">Melvin E. Conway</a></p></blockquote><p></p><blockquote><p>the modular decomposition of a system and the decomposition of the development organization must be done togethe</p><p>- <a href="https://martinfowler.com/bliki/ConwaysLaw.html">Martin Fowler</a></p></blockquote><p>That is why distance belongs in the formula. Two equally coupled systems can produce very different pain depending on how far a change must travel across human and technical boundaries.</p><p>The longest distance in practice is often not another repository. It is another team. And even that is not the limit. The most expensive case is often another team with different incentives, different roadmaps, different operational responsibilities, and only partial ownership of the outcome.</p><p>At that point, what hurts is no longer just dependency in code. What hurts is dependency in the socio-technical system.</p><h2>Conway&#8217;s Law explains why this is hard to fix</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8EPY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb34144e-adc7-45f6-9d35-cee7d5bae381_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8EPY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb34144e-adc7-45f6-9d35-cee7d5bae381_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!8EPY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb34144e-adc7-45f6-9d35-cee7d5bae381_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!8EPY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb34144e-adc7-45f6-9d35-cee7d5bae381_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!8EPY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb34144e-adc7-45f6-9d35-cee7d5bae381_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8EPY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb34144e-adc7-45f6-9d35-cee7d5bae381_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cb34144e-adc7-45f6-9d35-cee7d5bae381_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3160484,&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://emmanuelvalverderamos.substack.com/i/189802324?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb34144e-adc7-45f6-9d35-cee7d5bae381_1408x752.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_!8EPY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb34144e-adc7-45f6-9d35-cee7d5bae381_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!8EPY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb34144e-adc7-45f6-9d35-cee7d5bae381_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!8EPY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb34144e-adc7-45f6-9d35-cee7d5bae381_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!8EPY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb34144e-adc7-45f6-9d35-cee7d5bae381_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>One of the reasons these problems persist is that software architecture and organizational structure are not independent. Systems tend to mirror the communication paths of the organizations that build them. This insight matters because it explains why awkward team boundaries often become awkward software boundaries, and vice versa.</p><blockquote><p>organizations which design systems ... are constrained to produce designs which are copies of the communication structures of these organizations</p><p>- <a href="https://www.melconway.com/research/committees.html">Melvin E. Conway</a></p></blockquote><p>If teams are split in ways that do not match the real shape of change, the software will usually reflect that mismatch. Interfaces become negotiation artifacts. Ownership becomes ambiguous. Work starts in one place and finishes in another. No single team can move quickly because every meaningful change has been spread across the org chart.</p><p>This is why many architectural problems survive technical refactoring. The technical structure is being regenerated by the organizational structure that surrounds it. If communication paths force fragmentation, design fragmentation will keep reappearing.</p><p>Distance, then, is not accidental. It is often built into the system by the way responsibility has been distributed.</p><h2>Anemic domains and the cost of externalized behavior</h2><p>This becomes especially painful when a domain is structurally weak. An anemic domain model is a familiar example. The data exists, but the behavior and business rules that should live with that data have been pushed elsewhere. On the surface, ownership appears clear. In reality, the true responsibility is distributed across service layers, orchestration code, external teams, and ad hoc coordination.</p><p>That creates dependency by design.</p><p>A team may appear to own a domain, but another team holds crucial rules. Or the data sits in one bounded context while the real decisions are made in another. This is where code smells like Feature Envy become highly relevant. If behavior constantly reaches across a boundary to manipulate another part of the model, that is often a sign that the boundary is wrong.</p><p>The software smell and the organizational smell are mirrors of each other. One team appears autonomous, but cannot finish work without another team&#8217;s logic, approval, or intervention. The domain looks split, yet the actual change path reveals hidden dependency.</p><p>This is exactly where pain becomes chronic. High coupling, high volatility, and long distance reinforce one another. A domain that changes often but cannot act on its own becomes an engine of delay.</p><h2>Why this is a Lean problem</h2><p>Lean is often reduced to efficiency rhetoric, but the deeper Lean concern is flow. Not local busyness. Not apparent utilization. Flow.</p><p>A change request creates value only when it moves through the system and reaches reality. The key question is not whether everyone is occupied. The key question is whether work can move with low friction, low waiting time, low rework, and low coordination overhead.</p><p>That makes the pain formula deeply Lean.</p><p>Coupling interrupts flow because it causes work to spread.</p><p>Volatility increases load on the path of change because the same route is used repeatedly.</p><p>Distance injects waiting, handoffs, translation, and queueing.</p><p>From a Lean perspective, pain is what happens when the path of value is too fragmented to move smoothly.</p><p>This is why handoffs are so dangerous. Every handoff is a transfer of work or responsibility across a boundary, and every such transfer is a chance for delay, context loss, misalignment, or reprioritization. Not all handoffs are avoidable, but systems with too many of them pay a coordination tax on every change.</p><p>The ideal is not zero collaboration. The ideal is that the path of ordinary change stays as local as reasonably possible. That is one of the most useful ways to read stream aligned thinking. A team aligned to a flow of work should be able to complete meaningful change end to end without excessive external dependence. That is not merely a team design principle. It is an attempt to reduce distance.</p><p>Lean also clarifies something else. Excessive collaboration is not always a sign of health. Sometimes it is the symptom. If every feature requires half the company to coordinate, the problem may not be that people need to collaborate more. The problem may be that the system forces too many people into the same change path.</p><h2>Why this is also an Extreme Programming problem</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!P-5C!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17eb069-248a-4c93-8965-a4701783bdca_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!P-5C!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17eb069-248a-4c93-8965-a4701783bdca_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!P-5C!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17eb069-248a-4c93-8965-a4701783bdca_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!P-5C!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17eb069-248a-4c93-8965-a4701783bdca_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!P-5C!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17eb069-248a-4c93-8965-a4701783bdca_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!P-5C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17eb069-248a-4c93-8965-a4701783bdca_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f17eb069-248a-4c93-8965-a4701783bdca_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7840065,&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://emmanuelvalverderamos.substack.com/i/189802324?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17eb069-248a-4c93-8965-a4701783bdca_2816x1536.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_!P-5C!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17eb069-248a-4c93-8965-a4701783bdca_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!P-5C!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17eb069-248a-4c93-8965-a4701783bdca_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!P-5C!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17eb069-248a-4c93-8965-a4701783bdca_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!P-5C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17eb069-248a-4c93-8965-a4701783bdca_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Extreme Programming becomes much more coherent when read through the formula.</p><p>XP is often introduced through practices like pair programming, test driven development, refactoring, continuous integration, simple design, and small releases. But underneath those practices is a deeper goal: reduce the cost and distance of change.</p><p>Pair programming reduces knowledge distance, and reduces the risk. It keeps critical understanding from concentrating in one person and reduces the chance that one specialist becomes a bottleneck.</p><p>Test driven development reduces the fear of local change. It allows teams to move structure safely and keep refactoring affordable in volatile areas.</p><p>Continuous integration reduces temporal distance. Work does not stay isolated for long. Integration happens early, when problems are cheaper and clearer.</p><p>Small releases reduce feedback distance. The time between implementation and validation becomes shorter.</p><p>Simple design reduces accidental coupling. The system carries less speculative complexity and therefore exposes fewer unnecessary dependencies.</p><p>Refactoring reduces structural distance. It moves behavior closer to where it belongs, collapses accidental indirection, and shortens the path ordinary changes must travel.</p><p>Seen this way, XP is not a collection of craft preferences. It is a practical response to the economics of pain. It tries to keep change local, reversible, testable, and fast. In other words, it is an attempt to reduce one or more terms in the formula.</p><p>This is why XP remains so relevant in socio-technical discussions. It does not only improve code quality. It improves the shape of movement through the system.</p><h2>The hidden cost of basal weight</h2><p>There is another concept that helps explain why systems become heavier over time: basal cost. Every feature that exists imposes an ongoing burden. It must be understood, maintained, tested, kept compatible, monitored, and mentally carried by the people who work in the codebase.</p><p>Even a harmless addition is not free once shipped. It becomes part of the terrain future changes must cross.</p><p>This matters because basal cost amplifies the pain formula. High volatility in a system with growing structural weight becomes more expensive. More concepts mean more places to understand. More dependencies mean more possible side effects. More retained complexity means a longer path to confidence.</p><p>This is one of the reasons YAGNI matters beyond stylistic simplicity. Premature complexity does not only waste effort today. It increases the future cost of motion. It adds cost of carry before the business has even received value from it.</p><p>Pain is not generated only by bad changes. It is also generated by too much unnecessary software already existing.</p><h2>Corp smells are often path smells</h2><p>Once you think in terms of coupling, volatility, and distance, many familiar organizational problems become easier to interpret.</p><p>A team that cannot finish work without another team&#8217;s repeated intervention is often showing a form of Feature Envy at organizational scale.</p><p>A change that turns into many scattered tickets, cross team approvals, and fragmented edits is often Shotgun Surgery expressed as delivery flow.</p><p>A team or individual that everything must pass through behaves like a God Class in social form. The problem is not competence. The problem is structural concentration.</p><p>A team boundary that exists formally but does not match the actual path of responsibility resembles Refused Bequest at organizational level. The declared ownership and the real ownership do not match.</p><p>These are not just cultural issues. They are path issues. They tell you that the route ordinary change must travel has been distorted.</p><p>This is why corp smells are useful. They shift the conversation away from personalities and toward structure. Friction is often not caused by people being difficult. It is caused by the environment forcing unnecessary dependency and coordination.</p><h2>The practical meaning of autonomy</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!l9_9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a350e11-d792-4d8f-a4ea-01353e11dc4b_2816x1368.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!l9_9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a350e11-d792-4d8f-a4ea-01353e11dc4b_2816x1368.png 424w, https://substackcdn.com/image/fetch/$s_!l9_9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a350e11-d792-4d8f-a4ea-01353e11dc4b_2816x1368.png 848w, https://substackcdn.com/image/fetch/$s_!l9_9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a350e11-d792-4d8f-a4ea-01353e11dc4b_2816x1368.png 1272w, https://substackcdn.com/image/fetch/$s_!l9_9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a350e11-d792-4d8f-a4ea-01353e11dc4b_2816x1368.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!l9_9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a350e11-d792-4d8f-a4ea-01353e11dc4b_2816x1368.png" width="2816" height="1368" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6a350e11-d792-4d8f-a4ea-01353e11dc4b_2816x1368.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1368,&quot;width&quot;:2816,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6829605,&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://emmanuelvalverderamos.substack.com/i/189802324?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e4d17a7-833b-4bfe-ad45-9f78c2ffa485_2816x1536.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_!l9_9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a350e11-d792-4d8f-a4ea-01353e11dc4b_2816x1368.png 424w, https://substackcdn.com/image/fetch/$s_!l9_9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a350e11-d792-4d8f-a4ea-01353e11dc4b_2816x1368.png 848w, https://substackcdn.com/image/fetch/$s_!l9_9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a350e11-d792-4d8f-a4ea-01353e11dc4b_2816x1368.png 1272w, https://substackcdn.com/image/fetch/$s_!l9_9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a350e11-d792-4d8f-a4ea-01353e11dc4b_2816x1368.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Autonomy is often treated as a vague aspiration, but in this context it can be defined very concretely: the ability of a team to make and deliver meaningful changes without excessive dependence on other teams.</p><p>That definition matters because it connects autonomy directly to pain.</p><p>If a team lacks autonomy, one of three things is usually true. The coupling is too high. The distance is too large. Or the team owns an area whose cohesion is too low to support local change.</p><p>A team cannot be considered truly autonomous if it needs another team for every rule change, another team for every release, another team for every production adjustment, and a specialist outside the team to interpret the domain every time something important moves.</p><p>Autonomy is not isolation. Teams still need collaboration, shared standards, and mutual support. But high quality autonomy means that the default path for ordinary change stays short.</p><p>This is one of the strongest organizational consequences of the formula. It turns autonomy from a slogan into a design criterion.</p><h2>How to use the formula diagnostically</h2><p>The real usefulness of the formula is not rhetorical. It improves diagnosis.</p><p>When a change hurts, ask three questions.</p><p><strong>What is tightly coupled that should remain local?</strong></p><p>This question reveals structural spread. It helps identify behavior living too far from its data, rules scattered across layers, awkward cross service dependencies, or responsibilities fragmented across teams.</p><p><strong>Where is volatility actually concentrated?</strong></p><p>This question reveals whether design effort is being spent in the right places. It distinguishes stable areas from high change zones and exposes where structural weakness will be repaid repeatedly.</p><p><strong>How many boundaries is this change crossing, and what kind are they?</strong></p><p>This question reveals distance. Not just in code, but in people, process, semantics, release ownership, and coordination needs.</p><p>These questions are often more useful than asking whether the team needs more process, more collaboration, or more alignment meetings. Those things may help temporarily, but if the path of change itself is wrong, the real answer is structural.</p><p>The path needs refactoring.</p><h2>Reducing pain means changing the path of change</h2><p>There are only a few honest ways to reduce pain, and all of them involve changing the shape of movement in the system.</p><p>The first is to reduce coupling. Move behavior closer to the rules and data it changes with. Collapse accidental dependencies. Stop distributing one responsibility across too many layers, components, or teams.</p><p>The second is to reduce volatility exposure. Do not spend equal architectural effort everywhere. Invest in flexibility where change is frequent. Keep highly volatile parts from being entangled with broad and expensive dependency chains.</p><p>The third is to reduce distance. Shorten the path a change must travel. Reduce handoffs. Bring implementation, operational knowledge, and domain responsibility closer together where that makes sense. Align team boundaries with the real flow of work rather than with historical convenience or abstract functional silos.</p><p>This is where practical techniques matter. Pairing reduces knowledge distance. Rotation exposes concentrated expertise and fragile ownership. Cross functional teams reduce handoff distance. Smaller batch sizes reduce the risk of hidden coupling going unnoticed. Refactoring keeps changes local. Continuous integration reduces the cost of delayed convergence. Richer domain models reduce the need for external orchestration.</p><p>Notice what these practices have in common. They do not merely improve output. They compress the path of change.</p><h2>The hardest lesson</h2><p>The hardest lesson in all of this is that <strong>collaboration itself is not always the remedy. Sometimes it is the price of bad boundaries</strong>.</p><p>Many organizations respond to dependency pain by adding more meetings, more alignment rituals, more coordination roles, and more communication layers. Sometimes these are necessary. But often they are coping mechanisms. The system is already telling you that the ordinary path of change is too long. Extra process may help people survive it, but it rarely makes it cheap.</p><p>The deeper solution is to redesign the path so that less coordination is needed for ordinary work.</p><p>That is why this formula matters. It pulls the conversation back to first principles. Where does change start, where does it spread, how often does it happen, and how far must it travel before value reaches the user?</p><p><strong>If the answer is &#8220;too far,&#8221; the organization does not only have a delivery problem. It has a design problem.</strong></p><h2>Glossary</h2><p><strong>Refactoring</strong><br>Refactoring is a disciplined way to improve the internal structure of existing code while preserving observable behavior.<br>Source: <a href="https://martinfowler.com/books/refactoring.html">https://martinfowler.com/books/refactoring.html</a></p><p><strong>Code smell</strong><br>A code smell is a surface indication that usually points to a deeper structural problem.<br>Source: <a href="https://martinfowler.com/bliki/CodeSmell.html">https://martinfowler.com/bliki/CodeSmell.html</a></p><p><strong>Anemic domain model</strong><br>An anemic domain model is a model where data exists but behavior and business rules are pushed outside it. Fowler treats it as an anti-pattern.<br>Source: <a href="https://martinfowler.com/bliki/AnemicDomainModel.html">https://martinfowler.com/bliki/AnemicDomainModel.html</a></p><p><strong>Conway&#8217;s Law</strong><br>Organizations that design systems tend to produce designs that mirror their communication structures.<br>Source: <a href="https://www.melconway.com/Home/Committees_Paper.html">https://www.melconway.com/Home/Committees_Paper.html</a></p><p><strong>Socio-technical congruence</strong><br>The fit between the coordination required by technical dependencies and the coordination that actually happens between the people doing the work.<br>Source: <a href="https://herbsleb.org/web-pubs/pdfs/cataldo-socio-2008.pdf">https://herbsleb.org/web-pubs/pdfs/cataldo-socio-2008.pdf</a></p><p><strong>Basal cost</strong><br>The ongoing cost of a feature simply existing in the system after it has been built.<br>Source: <a href="https://www.eferro.net/2021/02/basal-cost-of-software.html">https://www.eferro.net/2021/02/basal-cost-of-software.html</a></p><p><strong>YAGNI</strong><br>A principle from Extreme Programming against building speculative capability too early.<br>Source: <a href="https://martinfowler.com/bliki/Yagni.html">https://martinfowler.com/bliki/Yagni.html</a></p><p><strong>Stream-aligned team</strong><br>A team aligned to a flow of work or business domain with end to end ownership.<br>Source: <a href="https://teamtopologies.com/key-concepts">https://teamtopologies.com/key-concepts</a></p><h2>Conclusion</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sYAA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd630dece-0074-4782-98ab-daafabbb5399_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sYAA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd630dece-0074-4782-98ab-daafabbb5399_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!sYAA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd630dece-0074-4782-98ab-daafabbb5399_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!sYAA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd630dece-0074-4782-98ab-daafabbb5399_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!sYAA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd630dece-0074-4782-98ab-daafabbb5399_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sYAA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd630dece-0074-4782-98ab-daafabbb5399_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d630dece-0074-4782-98ab-daafabbb5399_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1421103,&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://emmanuelvalverderamos.substack.com/i/189802324?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd630dece-0074-4782-98ab-daafabbb5399_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sYAA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd630dece-0074-4782-98ab-daafabbb5399_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!sYAA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd630dece-0074-4782-98ab-daafabbb5399_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!sYAA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd630dece-0074-4782-98ab-daafabbb5399_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!sYAA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd630dece-0074-4782-98ab-daafabbb5399_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Most painful software systems are not painful because change exists. They are painful because change must travel through structures that were not designed for the real shape of movement.</p><p><strong>Coupling</strong> spreads the change.</p><p><strong>Volatility</strong> makes you pay for that spread repeatedly.</p><p><strong>Distance</strong> turns every propagation into coordination cost.</p><blockquote><p>for each desired change, make the change easy (<em><strong>warning</strong>: this may be hard</em>), then make the easy change</p><p>- <a href="https://x.com/KentBeck/status/250733358307500032">Kent Beck</a></p></blockquote><p>That is why software pain is rarely just a code quality issue. It is economic, because future change is where cost accumulates. It is social, because dependencies consume attention, time, and organizational capacity. It is technical, because structure determines how far work must propagate. And it is socio-technical, because the architecture of the software and the architecture of the organization reinforce one another, <strong>and if anybody thinks that AI will solve this, I can already answer and say NO it won&#8217;t.</strong></p><p>This is also why refactoring should be understood more broadly. Refactoring is not only about making code cleaner. It is about redesigning the path of change so that ordinary work can remain local, comprehensible, and cheap. Sometimes that means changing classes or modules. Sometimes it means changing team boundaries, ownership, release flows, or where domain responsibility actually lives.</p><p>The formula matters because it gives us a rigorous and practical way to see this.</p><div class="callout-block" data-callout="true"><p style="text-align: center;"><strong>Pain = Coupling &#215; Volatility &#215; Distance</strong></p></div><p>Not as a universal law. Not as literal mathematics. But as a sharp lens.</p><p>A lens for seeing why some systems absorb change gracefully while others turn every minor request into a cross functional campaign.</p><p>A lens for understanding why autonomy matters, why rich boundaries matter, why handoffs matter, why Lean cares about flow, and why Extreme Programming keeps insisting on small, local, fast, reversible change.</p><p>And above all, a lens for remembering the real question.</p><p>Not &#8220;how hard was this feature to build?&#8221;</p><p>But &#8220;how far did this change have to travel, and why?&#8221;</p><h2>References</h2><ol><li><p><strong>Boehm, Barry W.</strong> <em>Software Engineering Economics</em>. Prentice-Hall, 1981. Bibliographic record confirms the title, publisher, year, and ISBN. (<a href="https://books.google.com/books/about/Software_Engineering_Economics.html?id=VphQAAAAMAAJ">Google Books</a>)</p></li><li><p><strong>Fowler, Martin.</strong> <em>Refactoring: Improving the Design of Existing Code</em>. The official book page describes refactoring as behavior-preserving transformation and positions code smells and testing as central parts of the book. (<a href="https://martinfowler.com/books/refactoring.html">martinfowler.com</a>)</p></li><li><p><strong>Fowler, Martin.</strong> &#8220;Code Smell.&#8221; <em>martinfowler.com</em>. Canonical definition of code smell as a surface indication of a deeper problem. (<a href="https://martinfowler.com/bliki/CodeSmell.html">martinfowler.com</a>)</p></li><li><p><strong>Fowler, Martin.</strong> &#8220;Is High Quality Software Worth the Cost?&#8221; <em>martinfowler.com</em>. Core source for the argument that high internal quality reduces the cost of future features. (<a href="https://martinfowler.com/articles/is-quality-worth-cost.html">martinfowler.com</a>)</p></li><li><p><strong>Fowler, Martin.</strong> &#8220;Yagni.&#8221; <em>martinfowler.com</em>. Canonical source for YAGNI as an Extreme Programming mantra against building speculative capability too early. (<a href="https://martinfowler.com/bliki/Yagni.html">martinfowler.com</a>)</p></li><li><p><strong>Fowler, Martin.</strong> &#8220;Anemic Domain Model.&#8221; Referenced via Fowler&#8217;s Domain-Driven Design index page, which lists the article and describes it as an anti-pattern. (<a href="https://martinfowler.com/tags/domain%20driven%20design.html">martinfowler.com</a>)</p></li><li><p><strong>Conway, Melvin E.</strong> &#8220;How Do Committees Invent?&#8221; <em>Datamation</em>, April 1968. Official page and original paper hosted on Conway&#8217;s site. (<a href="https://www.melconway.com/Home/Committees_Paper.html">melconway.com</a>)</p></li><li><p><strong>Cataldo, Marcelo, James D. Herbsleb, and Kathleen M. Carley.</strong> &#8220;Socio-Technical Congruence: A Framework for Assessing the Impact of Technical and Work Dependencies on Software Development Productivity.&#8221; 2008. Official PDF hosted on Herbsleb&#8217;s site. (<a href="https://herbsleb.org/web-pubs/pdfs/cataldo-socio-2008.pdf">herbsleb.org</a>)</p></li><li><p><strong>Ferro, Eduardo.</strong> &#8220;Basal Cost of software.&#8221; Eduardo Ferro&#8217;s original article introducing the idea of basal cost in software. (<a href="https://www.eferro.net/2021/02/basal-cost-of-software.html">eferro.net</a>)</p></li><li><p><strong>Thoughtworks Technology Podcast.</strong> &#8220;Exploring the Basal Cost of software.&#8221; Thoughtworks discussion that explicitly credits Eduardo Ferro with coining the term and expands the idea as a software maintenance metaphor. (<a href="https://www.thoughtworks.com/en-de/insights/podcasts/technology-podcasts/exploring-basal-cost-software">Thoughtworks</a>)</p></li><li><p><strong>Evans, Eric.</strong> <em>Domain-Driven Design: Tackling Complexity in the Heart of Software</em>. Addison-Wesley Professional, 2004. Bibliographic record confirms title, publisher, year, and ISBN. (<a href="https://books.google.com/books/about/Domain_driven_Design.html?id=7dlaMs0SECsC">Google Books</a>)</p></li><li><p><strong>Beck, Kent.</strong> <em>Extreme Programming Explained: Embrace Change</em>. Addison-Wesley Professional. Bibliographic records confirm the book and its publisher across editions. (<a href="https://books.google.com/books?id=G8EL4H4vf7UC">books.google.com</a>)</p></li><li><p><strong>Skelton, Matthew, and Manuel Pais.</strong> <em>Team Topologies: Organizing Business and Technology Teams for Fast Flow</em>. IT Revolution, 2019. Official Team Topologies book page and bibliographic records confirm title, publisher, and publication details. (<a href="https://teamtopologies.com/book">Team Topologies</a>)</p></li><li><p><strong>Team Topologies.</strong> &#8220;Key Concepts.&#8221; Official source for stream-aligned teams, end-to-end ownership, and fast flow of change. (<a href="https://teamtopologies.com/key-concepts?utm_source=chatgpt.com">Team Topologies</a>)</p></li></ol>]]></content:encoded></item><item><title><![CDATA[The lie inside the estimate]]></title><description><![CDATA[Fake certainty starts when a weak prediction is asked to replace direction, visibility, and trust]]></description><link>https://emmanuelvalverderamos.substack.com/p/the-lie-inside-the-estimate</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/the-lie-inside-the-estimate</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Mon, 01 Jun 2026 13:41:13 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Rz_F!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Rz_F!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Rz_F!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!Rz_F!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!Rz_F!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!Rz_F!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Rz_F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:899806,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.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_!Rz_F!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!Rz_F!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!Rz_F!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!Rz_F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe327d1-385b-4257-813c-eff43cddcfd1_1024x558.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Imagine stopping someone in Barcelona, pointing at the Sagrada Fam&#237;lia, and asking:</p><p>&#8220;How long would it take you to finish this?&#8221;</p><p>The question sounds practical. Time matters. Money matters. People need to coordinate. Someone has to decide whether the work is worth funding. But the question hides a trap: it treats creation as if it were only execution.</p><p>A builder can estimate how long it takes to move stone, prepare scaffolding, install a known piece, or repeat a technique that is already understood. That kind of thinking can help. But the real work is not only placing material in the right order. The real work includes design interpretation, technical constraints, funding, public pressure, changing methods, damaged knowledge, architectural judgment, and decisions that only become visible once part of the work exists.</p><p>The Sagrada Fam&#237;lia makes that visible in stone.</p><p>Construction began in 1882. More than 140 years later, the building is still being completed. It has not simply been &#8220;late&#8221; once. During the Spanish Civil War, plans and photographs were burnt and plaster models were smashed. Later teams had to continue from what could be saved, reconstructed, interpreted, and adapted.</p><p>Now imagine the estimate had been written on the wall before all of that happened.</p><p>The basilica will be finished in six years.</p><p>Every discovery is now a delay. Every reconstruction is now a failure. Every funding constraint is now bad planning. Every change in technique is now suspicious. Every new decision is treated as deviation from a future that nobody had actually seen.</p><p>That is much closer to software.</p><p>Software is not only execution. Software is creation inside constraints. It is discovery inside a changing system. It is not placing identical bricks in a straight line after every brick has already been understood. It is closer to building something whose final shape becomes clearer while the work exists, while the context changes, while people learn, and while the meaning of &#8220;finished&#8221; itself has to be negotiated.</p><p>Sometimes it is even worse. Sometimes the question is not &#8220;How long will it take to finish this known piece?&#8221; but &#8220;How long will it take to finish the whole Sagrada Fam&#237;lia, including the parts we do not fully understand yet, under constraints that will change, with knowledge that may disappear, and with decisions that future teams will have to make?&#8221;</p><p>That is the kind of work many software teams are asked to estimate.</p><p>The recent history makes the metaphor even sharper. The Sagrada Fam&#237;lia had a 2026 completion target linked to the centenary of Gaud&#237;&#8217;s death. Then COVID stopped construction, visitor revenue collapsed, and the target had to be reconsidered. Even when the central Tower of Jesus Christ reached 172.5 metres in February 2026, that visible milestone did not mean the whole project was finished. Sculptures, decorative details, the Glory fa&#231;ade, and other work still remained.</p><p>One milestone did not turn the whole thing into certainty.</p><p>That is also software.</p><p>Someone asks for an estimate because they need visibility, coordination, confidence, or trust. That need is legitimate. People outside the team want to know whether the investment is moving, whether the work is blocked, whether customers are getting something useful, and whether the organization can make decisions responsibly.</p><p>But instead of creating visibility through real progress, close collaboration, and observable increments, many organizations create a number. Then the number becomes a date. The date becomes a promise. The promise becomes pressure. The pressure becomes the plan.</p><p>That is where estimating becomes a con.</p><p>Not because all forecasting is useless. Not because the future does not matter. Not because teams should hide behind uncertainty and refuse to help the business make decisions. The problem is not thinking about time, cost, risk, or investment. The problem is producing predictions that do not improve decisions, then treating those predictions as if they were knowledge.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hapL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08abeea-3c68-4f50-959a-3b1f5e8fc7fc_3107x604.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hapL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08abeea-3c68-4f50-959a-3b1f5e8fc7fc_3107x604.png 424w, https://substackcdn.com/image/fetch/$s_!hapL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08abeea-3c68-4f50-959a-3b1f5e8fc7fc_3107x604.png 848w, https://substackcdn.com/image/fetch/$s_!hapL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08abeea-3c68-4f50-959a-3b1f5e8fc7fc_3107x604.png 1272w, https://substackcdn.com/image/fetch/$s_!hapL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08abeea-3c68-4f50-959a-3b1f5e8fc7fc_3107x604.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hapL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08abeea-3c68-4f50-959a-3b1f5e8fc7fc_3107x604.png" width="1456" height="283" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b08abeea-3c68-4f50-959a-3b1f5e8fc7fc_3107x604.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:283,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:100459,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08abeea-3c68-4f50-959a-3b1f5e8fc7fc_3107x604.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_!hapL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08abeea-3c68-4f50-959a-3b1f5e8fc7fc_3107x604.png 424w, https://substackcdn.com/image/fetch/$s_!hapL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08abeea-3c68-4f50-959a-3b1f5e8fc7fc_3107x604.png 848w, https://substackcdn.com/image/fetch/$s_!hapL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08abeea-3c68-4f50-959a-3b1f5e8fc7fc_3107x604.png 1272w, https://substackcdn.com/image/fetch/$s_!hapL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb08abeea-3c68-4f50-959a-3b1f5e8fc7fc_3107x604.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Estimating becomes a con when it exists only to calm organizational anxiety. It becomes a con when it supports self-inflicted dates, artificial commitments, and push-based management. It becomes a con when the number does not improve a real decision, but still creates pressure, waste, and distortion.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fzVU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29d026b5-ccb3-4b0f-b681-7af64d973e3a_1855x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fzVU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29d026b5-ccb3-4b0f-b681-7af64d973e3a_1855x188.png 424w, https://substackcdn.com/image/fetch/$s_!fzVU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29d026b5-ccb3-4b0f-b681-7af64d973e3a_1855x188.png 848w, https://substackcdn.com/image/fetch/$s_!fzVU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29d026b5-ccb3-4b0f-b681-7af64d973e3a_1855x188.png 1272w, https://substackcdn.com/image/fetch/$s_!fzVU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29d026b5-ccb3-4b0f-b681-7af64d973e3a_1855x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fzVU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29d026b5-ccb3-4b0f-b681-7af64d973e3a_1855x188.png" width="1456" height="148" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/29d026b5-ccb3-4b0f-b681-7af64d973e3a_1855x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:148,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:25171,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29d026b5-ccb3-4b0f-b681-7af64d973e3a_1855x188.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_!fzVU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29d026b5-ccb3-4b0f-b681-7af64d973e3a_1855x188.png 424w, https://substackcdn.com/image/fetch/$s_!fzVU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29d026b5-ccb3-4b0f-b681-7af64d973e3a_1855x188.png 848w, https://substackcdn.com/image/fetch/$s_!fzVU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29d026b5-ccb3-4b0f-b681-7af64d973e3a_1855x188.png 1272w, https://substackcdn.com/image/fetch/$s_!fzVU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29d026b5-ccb3-4b0f-b681-7af64d973e3a_1855x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>TL;DR</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LHnn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3820741-a44d-48b1-b443-d5788ef7a218_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LHnn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3820741-a44d-48b1-b443-d5788ef7a218_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!LHnn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3820741-a44d-48b1-b443-d5788ef7a218_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!LHnn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3820741-a44d-48b1-b443-d5788ef7a218_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!LHnn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3820741-a44d-48b1-b443-d5788ef7a218_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LHnn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3820741-a44d-48b1-b443-d5788ef7a218_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a3820741-a44d-48b1-b443-d5788ef7a218_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1556548,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3820741-a44d-48b1-b443-d5788ef7a218_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LHnn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3820741-a44d-48b1-b443-d5788ef7a218_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!LHnn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3820741-a44d-48b1-b443-d5788ef7a218_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!LHnn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3820741-a44d-48b1-b443-d5788ef7a218_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!LHnn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3820741-a44d-48b1-b443-d5788ef7a218_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p>Estimation is not the same as planning. Planning is deciding how to invest under uncertainty</p></li><li><p>The problem is not estimating. The problem is estimate inflation: asking an estimate to replace visibility, strategy, roadmap, commitment, trust, and control</p></li><li><p>A forecast is useful only when it improves a real decision</p></li><li><p>Estimating to produce prediction for nothing is waste</p></li><li><p>Estimating for self-inflicted dates turns uncertainty into pressure and pressure into counterfeit commitment</p></li><li><p>People outside the team often ask for estimates because they need visibility, not because they love estimation</p></li><li><p>Visibility does not come from predicting progress. Visibility comes from making progress observable</p></li><li><p>The argument that estimation helps teams understand complexity is weak. The useful part is the conversation, not the score</p></li><li><p>I do not consider estimation a valuable capability to invest in, because any improvement is too local, too context-dependent, and too hard to transfer across teams, products, companies, or codebases</p></li><li><p>If the goal is to understand complexity, do not estimate complexity. Attack it</p></li><li><p>Better estimates do not rescue weak product thinking. They only help the organization become wrong on schedule</p></li><li><p>Estimate-driven organizations often let estimates create dates, dates create timelines, timelines become roadmaps, and roadmaps pretend to be strategy</p></li><li><p>A healthier organization starts with direction, defines the change it wants to create, uses <em>Objectives and Key Results</em> (OKRs), <em>Key Results</em> (KRs), and <em>Key Performance Indicators</em> (KPIs) to inspect evidence, then chooses outputs as bets</p></li><li><p>Estimate-driven organizations tend to operate as push systems: scope, dates, and pressure are pushed into the team</p></li><li><p>Healthier organizations move toward pull: work is pulled according to capacity, finished according to explicit quality standards, and steered from evidence</p></li><li><p>Internal forecasts can be useful as private decision aids. External visibility should come from public progress</p></li><li><p>Output, outcome, and throughput give stronger visibility than estimation theater because they separate what was delivered, what changed, and how reliably the system finishes work</p></li></ul><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Index</h2><ol><li><p>The real need behind estimates is visibility</p></li><li><p>The number is not the knowledge</p></li><li><p>The estimate gets promoted too far</p></li><li><p>Demote the estimate</p></li><li><p>The conversation is useful. The score is the waste</p></li><li><p>Prediction for nothing</p></li><li><p>Self-inflicted dates and counterfeit commitment</p></li><li><p>Better estimates do not rescue weak product thinking</p></li><li><p>From estimate-driven timelines to strategy-led direction</p></li><li><p>Story points: time wearing a mask</p></li><li><p>Push: visibility through pressure</p></li><li><p>Pull: visibility through finished work</p></li><li><p>Public progress, private forecasts</p></li><li><p>Output, outcome, and throughput as a better language</p></li><li><p>The steering loop</p></li><li><p>A practical migration path</p></li><li><p>When forecasting can still help</p></li><li><p>Conclusions</p></li><li><p>References</p></li></ol><h2>The real need behind estimates is visibility</h2><p>Most arguments about estimation start too late. They begin with the estimate itself, as if the organization woke up one day with a strange love for numbers, planning poker, velocity charts, forecast meetings, and quarterly spreadsheets. That misses the deeper human and organizational need.</p><p>People ask for estimates because they want visibility. A customer wants to know whether their problem is being taken seriously. A director wants to know whether the investment is still worth funding. A sales team wants to know whether it can talk about a capability with confidence. A support team wants to know when it can stop explaining the same workaround. Finance wants to know whether money is being converted into something real.</p><p>Those needs are not stupid. They are not anti-agile. They are not management theater by themselves. The problem is that estimates often become the wrong answer to a valid question.</p><p>An estimate tries to create visibility by predicting the future. Real progress creates visibility by exposing the present. That exposure does not have to be a formal demo ceremony. Sometimes it is a customer trying a working slice. Sometimes it is a user validating part of a flow. Sometimes it is a feature flag enabled for a small group. Sometimes it is a conversation around newly released behavior. Sometimes it is a stakeholder seeing that a risky assumption has been resolved. Sometimes it is support confirming that a recurring pain has disappeared.</p><p>The format is not the point. The point is that reality enters the conversation early enough to improve the next decision.</p><p>This is one of the old promises of agile software development. Working software gives better visibility than abstract progress reports. Customer collaboration supports steering better than contract-style handoffs. Responding to change protects learning better than defending an outdated plan. Sustainable pace matters because prediction pressure often creates overload. Technical excellence matters because without quality, small slices and frequent exposure become unsafe.</p><p>That nuance matters. Plans, contracts, documentation, tools, and forecasts can still have value. The mistake is letting them dominate the things that make product work learnable: real software, close collaboration, feedback, and the ability to change direction.</p><p>If people outside the team need visibility, give them something stronger than a guessed date. Give them observable progress. Give them the chance to see what has changed. Give them the chance to say, &#8220;Yes, this is useful,&#8221; or &#8220;No, that is not the problem,&#8221; before the organization has spent three more months defending the wrong plan.</p><p>A number can calm anxiety for a moment. Evidence can change the decision.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IfKX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffba83ec0-b9e4-4d3b-aa4b-be173e1858ed_1675x812.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IfKX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffba83ec0-b9e4-4d3b-aa4b-be173e1858ed_1675x812.png 424w, https://substackcdn.com/image/fetch/$s_!IfKX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffba83ec0-b9e4-4d3b-aa4b-be173e1858ed_1675x812.png 848w, https://substackcdn.com/image/fetch/$s_!IfKX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffba83ec0-b9e4-4d3b-aa4b-be173e1858ed_1675x812.png 1272w, https://substackcdn.com/image/fetch/$s_!IfKX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffba83ec0-b9e4-4d3b-aa4b-be173e1858ed_1675x812.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IfKX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffba83ec0-b9e4-4d3b-aa4b-be173e1858ed_1675x812.png" width="1456" height="706" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fba83ec0-b9e4-4d3b-aa4b-be173e1858ed_1675x812.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:706,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:75098,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffba83ec0-b9e4-4d3b-aa4b-be173e1858ed_1675x812.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_!IfKX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffba83ec0-b9e4-4d3b-aa4b-be173e1858ed_1675x812.png 424w, https://substackcdn.com/image/fetch/$s_!IfKX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffba83ec0-b9e4-4d3b-aa4b-be173e1858ed_1675x812.png 848w, https://substackcdn.com/image/fetch/$s_!IfKX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffba83ec0-b9e4-4d3b-aa4b-be173e1858ed_1675x812.png 1272w, https://substackcdn.com/image/fetch/$s_!IfKX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffba83ec0-b9e4-4d3b-aa4b-be173e1858ed_1675x812.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>The number is not the knowledge</h2><p>Estimate culture starts to become dangerous when different concepts collapse into the same word. A forecast becomes a plan. A plan becomes a commitment. A commitment becomes a roadmap promise. A roadmap promise becomes strategy. The organization believes it has gained clarity, but it has often only renamed uncertainty.</p><p>These distinctions matter because each artifact has a different job.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xCzs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb00551a3-c6ec-4066-81ef-7701d775049d_1656x1051.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xCzs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb00551a3-c6ec-4066-81ef-7701d775049d_1656x1051.png 424w, https://substackcdn.com/image/fetch/$s_!xCzs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb00551a3-c6ec-4066-81ef-7701d775049d_1656x1051.png 848w, https://substackcdn.com/image/fetch/$s_!xCzs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb00551a3-c6ec-4066-81ef-7701d775049d_1656x1051.png 1272w, https://substackcdn.com/image/fetch/$s_!xCzs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb00551a3-c6ec-4066-81ef-7701d775049d_1656x1051.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xCzs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb00551a3-c6ec-4066-81ef-7701d775049d_1656x1051.png" width="1656" height="1051" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b00551a3-c6ec-4066-81ef-7701d775049d_1656x1051.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1051,&quot;width&quot;:1656,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:211512,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0c6dcc3-02db-4066-a9d3-36c8ed9d3a69_1720x1184.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_!xCzs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb00551a3-c6ec-4066-81ef-7701d775049d_1656x1051.png 424w, https://substackcdn.com/image/fetch/$s_!xCzs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb00551a3-c6ec-4066-81ef-7701d775049d_1656x1051.png 848w, https://substackcdn.com/image/fetch/$s_!xCzs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb00551a3-c6ec-4066-81ef-7701d775049d_1656x1051.png 1272w, https://substackcdn.com/image/fetch/$s_!xCzs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb00551a3-c6ec-4066-81ef-7701d775049d_1656x1051.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Software work suffers when these words collapse into one another. A team says, &#8220;This might take around six weeks.&#8221; Someone hears, &#8220;The team promised six weeks.&#8221; A stakeholder says, &#8220;We would like this by the end of the quarter.&#8221; Someone writes, &#8220;Committed for Q3.&#8221; A rough sizing conversation becomes a roadmap date. A roadmap date becomes a sales message. The sales message becomes a contract with reality.</p><p>Reality does not sign contracts.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!noYh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c3519e9-3ca9-4b7d-8910-92f16f126698_2389x652.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!noYh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c3519e9-3ca9-4b7d-8910-92f16f126698_2389x652.png 424w, https://substackcdn.com/image/fetch/$s_!noYh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c3519e9-3ca9-4b7d-8910-92f16f126698_2389x652.png 848w, https://substackcdn.com/image/fetch/$s_!noYh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c3519e9-3ca9-4b7d-8910-92f16f126698_2389x652.png 1272w, https://substackcdn.com/image/fetch/$s_!noYh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c3519e9-3ca9-4b7d-8910-92f16f126698_2389x652.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!noYh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c3519e9-3ca9-4b7d-8910-92f16f126698_2389x652.png" width="1456" height="397" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8c3519e9-3ca9-4b7d-8910-92f16f126698_2389x652.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:397,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:106560,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c3519e9-3ca9-4b7d-8910-92f16f126698_2389x652.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_!noYh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c3519e9-3ca9-4b7d-8910-92f16f126698_2389x652.png 424w, https://substackcdn.com/image/fetch/$s_!noYh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c3519e9-3ca9-4b7d-8910-92f16f126698_2389x652.png 848w, https://substackcdn.com/image/fetch/$s_!noYh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c3519e9-3ca9-4b7d-8910-92f16f126698_2389x652.png 1272w, https://substackcdn.com/image/fetch/$s_!noYh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c3519e9-3ca9-4b7d-8910-92f16f126698_2389x652.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is the common slide from uncertainty into obligation: a rough forecast is created when knowledge is weakest, then squeezed toward the date the organization wanted, and finally treated as a promise. The estimate is no longer a thinking tool. It is now a pressure container.</p><p>That is the con. Not the act of thinking about cost. Not the act of discussing risk. Not the act of asking, &#8220;Is this worth it?&#8221; Those are necessary. The con is when a weak forecast, produced under uncertainty, is dressed as certainty and used to control the people who must deal with the uncertainty.</p><p>A good estimate should make uncertainty visible. A bad estimate hides uncertainty behind a single number.</p><h2>The estimate gets promoted too far</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fMvK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5431ee-9ea9-4300-835b-db478c25b088_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fMvK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5431ee-9ea9-4300-835b-db478c25b088_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!fMvK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5431ee-9ea9-4300-835b-db478c25b088_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!fMvK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5431ee-9ea9-4300-835b-db478c25b088_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!fMvK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5431ee-9ea9-4300-835b-db478c25b088_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fMvK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5431ee-9ea9-4300-835b-db478c25b088_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/db5431ee-9ea9-4300-835b-db478c25b088_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:789284,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5431ee-9ea9-4300-835b-db478c25b088_1024x558.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_!fMvK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5431ee-9ea9-4300-835b-db478c25b088_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!fMvK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5431ee-9ea9-4300-835b-db478c25b088_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!fMvK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5431ee-9ea9-4300-835b-db478c25b088_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!fMvK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5431ee-9ea9-4300-835b-db478c25b088_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The estimate is not dangerous because it exists. It becomes dangerous when it is promoted beyond its competence.</p><p>A useful estimate has a narrow job: help a decision under uncertainty. It can help compare options, constrain investment, expose risk, or decide whether something is worth starting. It can help a team reason about capacity in the short term. It can help a business decide whether the next bet is economically sensible.</p><p>But in many organizations, the estimate is asked to do far more than that. It is expected to provide visibility, create trust, define the roadmap, secure stakeholder confidence, coordinate delivery, control teams, protect sales promises, and replace strategic clarity. That is too much weight for a guess.</p><p>This is the same failure pattern that happens when a roadmap is treated as strategy. Strategy should explain the challenge, the logic of response, the trade-offs, and why this direction makes sense. A roadmap should communicate staged movement and decision points. A timeline should schedule specific actions when enough is known. When those concepts collapse into one dated list of outputs, the organization gains a comforting artifact and loses clarity.</p><p>Estimation theater works the same way. Forecast, visibility, planning, roadmap, commitment, and strategy collapse into one number. The number becomes the visible surface of a decision that has not been properly made.</p><p>The organization no longer asks why this problem matters, why this sequence makes sense, why this investment deserves attention now, what must be true for the work to succeed, what would make the organization stop, what uncertainty is most expensive, or what is the smallest useful thing that can be learned. It asks only when.</p><p>That is how a forecast becomes a substitute for thinking.</p><p>The collapse is not inevitable, but it is common when an organization lacks clearer artifacts for strategy, roadmap, planning, commitment, and progress. Nobody says, &#8220;Let us replace strategy with a date.&#8221; The slide happens through meetings, dashboards, roadmaps, expectations, and pressure. A rough estimate becomes the anchor for more and more organizational meaning until the team is no longer discussing uncertainty. It is defending a number.</p><p>The estimate did not become more true. It became more politically expensive to question.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;98db909b-3a08-47ba-9e7f-435f3e86cf56&quot;,&quot;caption&quot;:&quot;The most common product artifact is also one of the most conceptually confused. Teams call a roadmap a strategy, then use a timeline as if it were a roadmap, and end up treating a schedule as if it were strategic thinking. That sounds harmless. It is not. Strategy, roadmap, tactics, and timeline answer different questions. Strategy defines the challenge&#8230;&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Product strategy is not a roadmap, and a roadmap is not a timeline&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-04-23T06:01:25.649Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!svn_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25522ba9-4aed-4b5a-a86e-fcde598ee1f1_1024x559.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/product-strategy-is-not-a-roadmap&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:194183324,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:3,&quot;comment_count&quot;:3,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>Demote the estimate</h2><p>The mature move is not abolition. It is demotion.</p><p>Do not make the estimate illegal. Make its job smaller and clearer. Do not ask it to replace visibility, strategy, roadmap, trust, or commitment. Do not ask it to prove that work is valuable. Do not ask it to make uncertain work certain. Do not ask it to become the emotional sedative of an anxious organization.</p><p>An estimate may help with a decision. That is all.</p><p>This distinction matters because many organizations oscillate between two weak positions. One side treats estimates as if they were the foundation of professional management. The other side reacts by treating all estimates as stupidity. Both positions are too crude.</p><p>The better position is narrower and more useful: use estimates only when they improve a decision. Do not use them as a substitute for evidence.</p><p>This turns estimation into an economic tool instead of a control ritual. The estimate must earn its cost. Before estimating, the organization should be able to answer a simple question: what will we decide differently after we know this?</p><p>If the answer is nothing, the estimate is probably waste. If the answer is that the number will be used to pressure the team, the estimate is worse than waste. If the answer is that the organization will choose between options, limit investment, reduce scope, sequence risk, decide whether to start, or decide whether to stop, the estimate may be useful.</p><p>Even then, it should carry assumptions, uncertainty, and an expiration date. It should not be smuggled into a promise.</p><p>A prediction can be emotionally useful and economically useless. That is why estimation theater survives. It gives anxious organizations something to hold. It makes uncertainty look legible. It makes leadership feel informed, stakeholders feel heard, and progress look managed.</p><p>But comfort is not clarity. The question is not whether the estimate reduces anxiety. The question is whether it improves judgment.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yWId!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd04876c5-a452-4163-9531-527a84f92f87_1172x1148.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yWId!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd04876c5-a452-4163-9531-527a84f92f87_1172x1148.png 424w, https://substackcdn.com/image/fetch/$s_!yWId!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd04876c5-a452-4163-9531-527a84f92f87_1172x1148.png 848w, https://substackcdn.com/image/fetch/$s_!yWId!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd04876c5-a452-4163-9531-527a84f92f87_1172x1148.png 1272w, https://substackcdn.com/image/fetch/$s_!yWId!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd04876c5-a452-4163-9531-527a84f92f87_1172x1148.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yWId!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd04876c5-a452-4163-9531-527a84f92f87_1172x1148.png" width="1172" height="1148" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d04876c5-a452-4163-9531-527a84f92f87_1172x1148.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1148,&quot;width&quot;:1172,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:96933,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd04876c5-a452-4163-9531-527a84f92f87_1172x1148.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_!yWId!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd04876c5-a452-4163-9531-527a84f92f87_1172x1148.png 424w, https://substackcdn.com/image/fetch/$s_!yWId!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd04876c5-a452-4163-9531-527a84f92f87_1172x1148.png 848w, https://substackcdn.com/image/fetch/$s_!yWId!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd04876c5-a452-4163-9531-527a84f92f87_1172x1148.png 1272w, https://substackcdn.com/image/fetch/$s_!yWId!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd04876c5-a452-4163-9531-527a84f92f87_1172x1148.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>The conversation is useful. The score is the waste.</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MP3E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe775cbd9-50a9-4aca-be79-545ac05923f3_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MP3E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe775cbd9-50a9-4aca-be79-545ac05923f3_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!MP3E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe775cbd9-50a9-4aca-be79-545ac05923f3_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!MP3E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe775cbd9-50a9-4aca-be79-545ac05923f3_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!MP3E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe775cbd9-50a9-4aca-be79-545ac05923f3_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MP3E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe775cbd9-50a9-4aca-be79-545ac05923f3_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e775cbd9-50a9-4aca-be79-545ac05923f3_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:878217,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe775cbd9-50a9-4aca-be79-545ac05923f3_1024x558.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_!MP3E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe775cbd9-50a9-4aca-be79-545ac05923f3_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!MP3E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe775cbd9-50a9-4aca-be79-545ac05923f3_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!MP3E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe775cbd9-50a9-4aca-be79-545ac05923f3_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!MP3E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe775cbd9-50a9-4aca-be79-545ac05923f3_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>One of the most common defenses of estimation is that it helps teams understand complexity and split work better.</p><p>I do not buy it.</p><p>If the goal is to split work well, start by looking for the smallest slice that can create value, learning, or visible progress for a user, customer, or stakeholder. That already forces the right conversation. You have to clarify behavior. You have to understand what matters. You have to decide what can be postponed. You have to ask what can be exposed earlier. You have to separate the valuable part from the surrounding noise.</p><p>You do not need to play &#8220;guess the complexity&#8221; to do that.</p><p>If the work feels too large, the answer is not to decide whether it is a five, an eight, or a thirteen. The answer is to spend that energy reducing uncertainty. Clarify the behavior. Explore the implementation options. Remove unknowns. Find the dependency that makes the work heavy. Look for a thinner path. Talk to the user. Talk to the customer. Talk to the people who understand the system. Reduce the work until it can be pulled responsibly.</p><p>That gives the team something better than an estimate. It gives the team knowledge.</p><p>The usual defense says that estimation creates conversation. Fine. But the conversation is valuable despite the score, not because of it. The useful part is discussing risk, uncertainty, dependencies, slicing, alternatives, and what &#8220;done&#8221; should mean. The weak part is pretending that the conversation needs to end with a number.</p><p>It does not.</p><p>The number is often the least valuable artifact in the room.</p><p>I also do not consider estimation a valuable capability to invest in. Not because people cannot become familiar with their own context, but because that familiarity is too local to be treated as a strong professional skill.</p><p>A team may become locally calibrated inside one product, one codebase, one domain, one architecture, and one group of people. That does not mean the same estimation ability travels well to another team, another product, another company, or another problem space. Even the same person may estimate differently when the codebase, business domain, team knowledge, quality of tests, architecture, dependencies, and operational constraints change.</p><p>That matters.</p><p>If a skill only works inside a narrow context, cannot be reliably transferred, does not produce customer value, and does not remove uncertainty by itself, then the return on investing in that skill is weak.</p><p>The apparent complexity of a task depends on too many local conditions: codebase familiarity, domain knowledge, test safety, team skills, design quality, dependencies, operational constraints, and how much hidden knowledge lives in people&#8217;s heads. The same task can be simple for one team and dangerous for another. The same developer can give a different answer in a different system, not because they became worse, but because the context changed.</p><p>So what exactly is the company investing in?</p><p>It is investing time in a ritual that does not reliably transfer, does not make the work safer, does not create customer value, and often produces less value than the conversation it was supposed to trigger. The team spends effort improving its ability to compare tasks against other tasks, when it could spend that effort making the task smaller, clearer, safer, and closer to delivery.</p><p>That is a bad trade.</p><p>The opportunity cost is real. Time spent scoring work is time not spent reducing unknowns, slicing better, testing an assumption, exposing progress, improving the design, or learning from a user. Worse, the score can create a false sense of security. The team leaves with a number, but not necessarily with more understanding.</p><p>That reduces liquidity. The team has less knowledge, fewer options, and less freedom to redirect because it invested in prediction instead of learning.</p><p>If the goal is to understand complexity, do not estimate complexity. Attack it. Make the work smaller. Make the behavior clearer. Make the risk visible. Make the next step safer. Make progress observable.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!57Pd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ee55d7-60c0-45df-8a68-d720c2c9f47d_3476x396.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!57Pd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ee55d7-60c0-45df-8a68-d720c2c9f47d_3476x396.png 424w, https://substackcdn.com/image/fetch/$s_!57Pd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ee55d7-60c0-45df-8a68-d720c2c9f47d_3476x396.png 848w, https://substackcdn.com/image/fetch/$s_!57Pd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ee55d7-60c0-45df-8a68-d720c2c9f47d_3476x396.png 1272w, https://substackcdn.com/image/fetch/$s_!57Pd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ee55d7-60c0-45df-8a68-d720c2c9f47d_3476x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!57Pd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ee55d7-60c0-45df-8a68-d720c2c9f47d_3476x396.png" width="1456" height="166" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79ee55d7-60c0-45df-8a68-d720c2c9f47d_3476x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:166,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:78007,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ee55d7-60c0-45df-8a68-d720c2c9f47d_3476x396.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_!57Pd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ee55d7-60c0-45df-8a68-d720c2c9f47d_3476x396.png 424w, https://substackcdn.com/image/fetch/$s_!57Pd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ee55d7-60c0-45df-8a68-d720c2c9f47d_3476x396.png 848w, https://substackcdn.com/image/fetch/$s_!57Pd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ee55d7-60c0-45df-8a68-d720c2c9f47d_3476x396.png 1272w, https://substackcdn.com/image/fetch/$s_!57Pd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ee55d7-60c0-45df-8a68-d720c2c9f47d_3476x396.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The better question is not, &#8220;How complex is this compared with something we remember?&#8221; The better question is, &#8220;What can we do now to make this smaller, clearer, safer, or more valuable?&#8221;</p><h2>Prediction for nothing</h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kqHs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cb8358-ee38-4dac-9068-8723077bdd50_2676x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kqHs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cb8358-ee38-4dac-9068-8723077bdd50_2676x188.png 424w, https://substackcdn.com/image/fetch/$s_!kqHs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cb8358-ee38-4dac-9068-8723077bdd50_2676x188.png 848w, https://substackcdn.com/image/fetch/$s_!kqHs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cb8358-ee38-4dac-9068-8723077bdd50_2676x188.png 1272w, https://substackcdn.com/image/fetch/$s_!kqHs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cb8358-ee38-4dac-9068-8723077bdd50_2676x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kqHs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cb8358-ee38-4dac-9068-8723077bdd50_2676x188.png" width="1456" height="102" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/73cb8358-ee38-4dac-9068-8723077bdd50_2676x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:102,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:40576,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cb8358-ee38-4dac-9068-8723077bdd50_2676x188.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_!kqHs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cb8358-ee38-4dac-9068-8723077bdd50_2676x188.png 424w, https://substackcdn.com/image/fetch/$s_!kqHs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cb8358-ee38-4dac-9068-8723077bdd50_2676x188.png 848w, https://substackcdn.com/image/fetch/$s_!kqHs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cb8358-ee38-4dac-9068-8723077bdd50_2676x188.png 1272w, https://substackcdn.com/image/fetch/$s_!kqHs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cb8358-ee38-4dac-9068-8723077bdd50_2676x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Prediction for nothing is one of the quietest wastes in software organizations.</p><p>It usually looks reasonable. A team is asked to estimate a backlog. Nobody knows whether all the items are needed. Nobody knows whether the customer will still want the same thing after the first release. Nobody knows which hidden dependency will matter. Nobody knows whether a simpler solution will appear after the first slice is built.</p><p>The ritual begins anyway. People discuss numbers. They compare similar stories. They argue whether something is a three or a five. They spend hours making uncertainty look tidy. At the end, the organization has a spreadsheet, a velocity, a date, and the comforting illusion that thinking has happened.</p><p>Sometimes no real decision changes. The organization was already going to do the work. The date was already desired. The team was already assigned. The scope was already politically loaded. The estimate did not change investment, sequencing, risk appetite, product strategy, or customer exposure. It only created a number that can later be used to ask why reality was disobedient.</p><p>That is prediction for nothing.</p><p>It is not harmless. Every estimation ritual consumes attention. It pulls skilled people away from slicing, clarifying, testing, designing, integrating, talking to users, and reducing real uncertainty. Worse, it teaches the organization to confuse numeric confidence with product learning.</p><p>The cost is not only the meeting. The cost is the mental model. A company that keeps asking for prediction without decision trains itself to value the appearance of control more than the evidence of progress. It becomes more interested in whether the map was drawn beautifully than whether the road is passable.</p><p>Prediction for nothing is waste because it does not change action. Prediction for pressure is worse because it changes action in the wrong direction.</p><h2>Self-inflicted dates and counterfeit commitment</h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E-7I!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdde32b6e-d2c0-47e0-8006-370adc852a75_1922x444.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E-7I!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdde32b6e-d2c0-47e0-8006-370adc852a75_1922x444.png 424w, https://substackcdn.com/image/fetch/$s_!E-7I!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdde32b6e-d2c0-47e0-8006-370adc852a75_1922x444.png 848w, https://substackcdn.com/image/fetch/$s_!E-7I!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdde32b6e-d2c0-47e0-8006-370adc852a75_1922x444.png 1272w, https://substackcdn.com/image/fetch/$s_!E-7I!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdde32b6e-d2c0-47e0-8006-370adc852a75_1922x444.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!E-7I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdde32b6e-d2c0-47e0-8006-370adc852a75_1922x444.png" width="1456" height="336" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dde32b6e-d2c0-47e0-8006-370adc852a75_1922x444.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:336,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:74254,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdde32b6e-d2c0-47e0-8006-370adc852a75_1922x444.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_!E-7I!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdde32b6e-d2c0-47e0-8006-370adc852a75_1922x444.png 424w, https://substackcdn.com/image/fetch/$s_!E-7I!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdde32b6e-d2c0-47e0-8006-370adc852a75_1922x444.png 848w, https://substackcdn.com/image/fetch/$s_!E-7I!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdde32b6e-d2c0-47e0-8006-370adc852a75_1922x444.png 1272w, https://substackcdn.com/image/fetch/$s_!E-7I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdde32b6e-d2c0-47e0-8006-370adc852a75_1922x444.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Some deadlines are real. A legal change can have an enforcement date. A conference has a fixed day. A migration may depend on a vendor shutdown. A contract can create an external constraint. These situations do not remove uncertainty, but they do change the decision problem.</p><p>Many dates in software are not like that. They are self-inflicted.</p><p>Someone wants the feature before the end of the quarter. Someone wants the launch aligned with an internal presentation. Someone wants the roadmap to look decisive. Someone promised something too early and now the team must convert that promise into software.</p><p>The danger is not merely that the date may be wrong. The danger is that the date changes the system&#8217;s behavior. When an artificial date becomes sacred, scope stops being a learning instrument and becomes a battlefield. Quality becomes negotiable. Testing becomes optional. Refactoring becomes &#8220;later.&#8221; Discovery becomes a threat. Bad news arrives late because the system punishes the people who reveal it early.</p><p>This is where commitment gets counterfeited.</p><p>Real commitment requires enough knowledge, agency, and responsibility to make a promise meaningful. Counterfeit commitment is different. It is extracted before enough is known, often from people who do not control the conditions required to make it true. It looks like alignment, but it is often just pressure with a signature.</p><p>The problem is not commitment. The problem is counterfeit commitment.</p><p>A real constraint should sharpen choices. It should force trade-offs. It should make the organization ask what must be smaller, safer, simpler, or earlier. It should create clarity about scope, quality, risk, and investment.</p><p>A self-inflicted date often protects wishful thinking. If the date is real, reduce scope, slice harder, expose risk earlier, and make trade-offs explicit. If the date is not real, be careful. You may not be planning. You may be manufacturing pressure and calling it alignment.</p><h2>Better estimates do not rescue weak product thinking</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!es35!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49a63a1b-77f3-45eb-a000-0c1192a4cd78_1789x491.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!es35!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49a63a1b-77f3-45eb-a000-0c1192a4cd78_1789x491.png 424w, https://substackcdn.com/image/fetch/$s_!es35!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49a63a1b-77f3-45eb-a000-0c1192a4cd78_1789x491.png 848w, https://substackcdn.com/image/fetch/$s_!es35!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49a63a1b-77f3-45eb-a000-0c1192a4cd78_1789x491.png 1272w, https://substackcdn.com/image/fetch/$s_!es35!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49a63a1b-77f3-45eb-a000-0c1192a4cd78_1789x491.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!es35!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49a63a1b-77f3-45eb-a000-0c1192a4cd78_1789x491.png" width="1456" height="400" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/49a63a1b-77f3-45eb-a000-0c1192a4cd78_1789x491.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:400,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:54899,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49a63a1b-77f3-45eb-a000-0c1192a4cd78_1789x491.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_!es35!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49a63a1b-77f3-45eb-a000-0c1192a4cd78_1789x491.png 424w, https://substackcdn.com/image/fetch/$s_!es35!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49a63a1b-77f3-45eb-a000-0c1192a4cd78_1789x491.png 848w, https://substackcdn.com/image/fetch/$s_!es35!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49a63a1b-77f3-45eb-a000-0c1192a4cd78_1789x491.png 1272w, https://substackcdn.com/image/fetch/$s_!es35!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49a63a1b-77f3-45eb-a000-0c1192a4cd78_1789x491.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There is a deeper trap hidden inside estimation culture: the belief that better prediction will fix weak judgment.</p><p>It will not.</p><p>A company can estimate accurately and still build the wrong thing. It can hit the roadmap date and miss the market. It can increase velocity and reduce value. It can deliver every promised feature and still fail to improve the customer&#8217;s situation.</p><p>Better estimates do not rescue weak product thinking. They only help the organization become wrong on schedule.</p><p>The mechanism is simple. The product problem is poorly understood, so the organization asks for delivery confidence instead of product clarity. The team estimates a solution before the problem has been properly shaped. The roadmap becomes a sequence of outputs. The conversation shifts from whether the work deserves to exist to whether it can be delivered on time.</p><p>This is how predictability becomes a trap. The organization becomes better at producing things that may not matter.</p><p>Delivery predictability is valuable when the work deserves to exist. It is dangerous when it accelerates the wrong bet. If the strategy is weak, the estimate becomes oversized because it is asked to compensate for unclear direction. People ask &#8220;when&#8221; because &#8220;why&#8221; was not resolved. They ask for confidence because the direction is not convincing. They ask for dates because the path lacks evidence.</p><p>That is not a planning problem alone. It is a product problem.</p><p>A mature organization does not use estimates to hide weak strategy. It uses strategy to decide what deserves attention, roadmaps to communicate staged movement, timelines when coordination is concrete enough, and forecasts only when they improve decisions.</p><p>Each artifact has a job. Confusion begins when one artifact is asked to do them all.</p><h2>From estimate-driven timelines to strategy-led direction</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JS8n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690d0161-c300-4f5e-b191-3c9fceaefaad_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JS8n!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690d0161-c300-4f5e-b191-3c9fceaefaad_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!JS8n!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690d0161-c300-4f5e-b191-3c9fceaefaad_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!JS8n!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690d0161-c300-4f5e-b191-3c9fceaefaad_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!JS8n!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690d0161-c300-4f5e-b191-3c9fceaefaad_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JS8n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690d0161-c300-4f5e-b191-3c9fceaefaad_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/690d0161-c300-4f5e-b191-3c9fceaefaad_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:719362,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690d0161-c300-4f5e-b191-3c9fceaefaad_1024x558.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_!JS8n!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690d0161-c300-4f5e-b191-3c9fceaefaad_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!JS8n!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690d0161-c300-4f5e-b191-3c9fceaefaad_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!JS8n!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690d0161-c300-4f5e-b191-3c9fceaefaad_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!JS8n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F690d0161-c300-4f5e-b191-3c9fceaefaad_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There is another failure mode hidden inside estimation culture. It does not stop at the team level. It climbs.</p><p>First, the organization asks for estimates. Then the estimates create dates. Then the dates create a timeline. Then the timeline becomes the roadmap. Then the roadmap starts pretending to be strategy.</p><p>At that point, the organization is no longer asking where it wants to go, why that direction matters, what must change, or what evidence would prove progress. It is managing a dated sequence of outputs and calling it direction.</p><p>That is dangerous because a timeline can coordinate action, but it cannot replace strategy. A timeline can say when something is expected to happen. It cannot explain why the problem matters, which trade-offs are acceptable, what outcome is being pursued, or what should happen if the evidence changes.</p><p>A strategy-led organization starts somewhere else. It starts by asking what problem matters, what direction makes sense, what change it wants to create, and what evidence would show that the investment is working. Only after that does it decide which outputs are worth building, which bets are worth funding, and which slices should be pulled next.</p><p>This is where <em>Objectives and Key Results</em> (OKRs), <em>Key Results</em> (KRs), and <em>Key Performance Indicators</em> (KPIs) matter. Not as corporate decoration, not as another reporting layer, and not as a way to make theater look measurable. They matter because they separate direction from delivery activity.</p><p>An output is something the team produces: a feature, a release, an integration, a migration, a workflow, an experiment, or a visible slice of product behavior. Outputs are necessary because nothing changes in reality until something is made, exposed, used, removed, simplified, or improved.</p><p>But outputs are not enough.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6djF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F599ba819-8794-42f0-aa35-4dabe3418e18_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6djF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F599ba819-8794-42f0-aa35-4dabe3418e18_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!6djF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F599ba819-8794-42f0-aa35-4dabe3418e18_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!6djF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F599ba819-8794-42f0-aa35-4dabe3418e18_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!6djF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F599ba819-8794-42f0-aa35-4dabe3418e18_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6djF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F599ba819-8794-42f0-aa35-4dabe3418e18_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/599ba819-8794-42f0-aa35-4dabe3418e18_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1009609,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F599ba819-8794-42f0-aa35-4dabe3418e18_1024x559.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_!6djF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F599ba819-8794-42f0-aa35-4dabe3418e18_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!6djF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F599ba819-8794-42f0-aa35-4dabe3418e18_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!6djF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F599ba819-8794-42f0-aa35-4dabe3418e18_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!6djF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F599ba819-8794-42f0-aa35-4dabe3418e18_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Outputs matter, but outputs are not the objective. Outputs are bets made in service of the objective. The objective is the change we want to create.</p><p>A team can ship the output and still miss the point. A company can deliver every item on the roadmap and still fail to improve the customer&#8217;s situation. A timeline can be accurate and still describe a journey to the wrong place.</p><p>That is why the organization needs a better chain of reasoning:</p><ul><li><p>Strategy defines where we want to go and why</p></li><li><p>Objectives express the direction of change we are trying to create</p></li><li><p><em>Key Results</em> (KRs) make that intended change inspectable</p></li><li><p><em>Key Performance Indicators</em> (KPIs) show whether important parts of the system are healthy or moving</p></li><li><p>Outputs are the concrete things we choose to build, change, expose, or remove in order to influence those results</p></li><li><p>Throughput tells us how reliably the system can finish meaningful work</p></li><li><p>Feedback tells us whether the work is actually helping<br>This reverses the estimation-driven model.</p></li></ul><p>In the estimation-driven model, the organization starts with work, estimates it, turns it into dates, builds a timeline, and then behaves as if the timeline were strategy. In the strategy-led model, the organization starts with direction, defines what change would matter, chooses outputs as bets, exposes progress early, and uses evidence to decide what to pull next.</p><p>The first model asks, &#8220;When will all these things be done?&#8221;</p><p>The second model asks, &#8220;Where are we trying to go, what would prove progress, and what is the next smallest thing worth doing?&#8221;</p><p>That difference matters.</p><p>If estimates create the timeline and the timeline creates the strategy, the organization is letting delivery prediction drive product direction. That is backwards. Estimation should not decide where the company goes. At most, it should help reason about the cost, risk, and uncertainty of possible paths.</p><p>The important thing is not whether the team can predict every output in advance. The important thing is whether the organization can connect its outputs to the outcomes and indicators that matter.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-3TV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a26254-0628-4f47-99bd-03bfdc4863f2_2462x724.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-3TV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a26254-0628-4f47-99bd-03bfdc4863f2_2462x724.png 424w, https://substackcdn.com/image/fetch/$s_!-3TV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a26254-0628-4f47-99bd-03bfdc4863f2_2462x724.png 848w, https://substackcdn.com/image/fetch/$s_!-3TV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a26254-0628-4f47-99bd-03bfdc4863f2_2462x724.png 1272w, https://substackcdn.com/image/fetch/$s_!-3TV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a26254-0628-4f47-99bd-03bfdc4863f2_2462x724.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-3TV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a26254-0628-4f47-99bd-03bfdc4863f2_2462x724.png" width="1456" height="428" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/53a26254-0628-4f47-99bd-03bfdc4863f2_2462x724.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;:87668,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a26254-0628-4f47-99bd-03bfdc4863f2_2462x724.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_!-3TV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a26254-0628-4f47-99bd-03bfdc4863f2_2462x724.png 424w, https://substackcdn.com/image/fetch/$s_!-3TV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a26254-0628-4f47-99bd-03bfdc4863f2_2462x724.png 848w, https://substackcdn.com/image/fetch/$s_!-3TV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a26254-0628-4f47-99bd-03bfdc4863f2_2462x724.png 1272w, https://substackcdn.com/image/fetch/$s_!-3TV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a26254-0628-4f47-99bd-03bfdc4863f2_2462x724.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Otherwise, it may become very good at delivering plans that should never have existed.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!A4-l!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88286bfb-5e39-4f44-abca-166ba76bc47e_1158x1836.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!A4-l!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88286bfb-5e39-4f44-abca-166ba76bc47e_1158x1836.png 424w, https://substackcdn.com/image/fetch/$s_!A4-l!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88286bfb-5e39-4f44-abca-166ba76bc47e_1158x1836.png 848w, https://substackcdn.com/image/fetch/$s_!A4-l!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88286bfb-5e39-4f44-abca-166ba76bc47e_1158x1836.png 1272w, https://substackcdn.com/image/fetch/$s_!A4-l!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88286bfb-5e39-4f44-abca-166ba76bc47e_1158x1836.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!A4-l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88286bfb-5e39-4f44-abca-166ba76bc47e_1158x1836.png" width="1158" height="1836" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/88286bfb-5e39-4f44-abca-166ba76bc47e_1158x1836.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1836,&quot;width&quot;:1158,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:149600,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88286bfb-5e39-4f44-abca-166ba76bc47e_1158x1836.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_!A4-l!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88286bfb-5e39-4f44-abca-166ba76bc47e_1158x1836.png 424w, https://substackcdn.com/image/fetch/$s_!A4-l!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88286bfb-5e39-4f44-abca-166ba76bc47e_1158x1836.png 848w, https://substackcdn.com/image/fetch/$s_!A4-l!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88286bfb-5e39-4f44-abca-166ba76bc47e_1158x1836.png 1272w, https://substackcdn.com/image/fetch/$s_!A4-l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88286bfb-5e39-4f44-abca-166ba76bc47e_1158x1836.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Once this mistake reaches strategy, it also changes how teams are measured. The organization stops asking whether the product is moving in the right direction and starts asking whether teams are producing enough points, closing enough tickets, and staying close enough to the plan. This is where story points often become time wearing a mask.</p><h2>Story points: time wearing a mask</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GRy_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78d56fc4-5185-408b-992a-d23b86fff737_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GRy_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78d56fc4-5185-408b-992a-d23b86fff737_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!GRy_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78d56fc4-5185-408b-992a-d23b86fff737_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!GRy_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78d56fc4-5185-408b-992a-d23b86fff737_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!GRy_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78d56fc4-5185-408b-992a-d23b86fff737_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GRy_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78d56fc4-5185-408b-992a-d23b86fff737_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/78d56fc4-5185-408b-992a-d23b86fff737_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:818741,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78d56fc4-5185-408b-992a-d23b86fff737_1024x558.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_!GRy_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78d56fc4-5185-408b-992a-d23b86fff737_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!GRy_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78d56fc4-5185-408b-992a-d23b86fff737_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!GRy_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78d56fc4-5185-408b-992a-d23b86fff737_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!GRy_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78d56fc4-5185-408b-992a-d23b86fff737_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The only defensible value of story points is the conversation they sometimes trigger. The points themselves are usually the least valuable part.</p><p>Inside a local team context, a conversation about size, uncertainty, and short-term capacity can be useful. But the dangerous part begins when the number escapes that context and becomes management currency.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hSo8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc485f339-d696-448b-905c-78249e6aba06_2559x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hSo8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc485f339-d696-448b-905c-78249e6aba06_2559x140.png 424w, https://substackcdn.com/image/fetch/$s_!hSo8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc485f339-d696-448b-905c-78249e6aba06_2559x140.png 848w, https://substackcdn.com/image/fetch/$s_!hSo8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc485f339-d696-448b-905c-78249e6aba06_2559x140.png 1272w, https://substackcdn.com/image/fetch/$s_!hSo8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc485f339-d696-448b-905c-78249e6aba06_2559x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hSo8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc485f339-d696-448b-905c-78249e6aba06_2559x140.png" width="1456" height="80" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c485f339-d696-448b-905c-78249e6aba06_2559x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:80,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28590,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc485f339-d696-448b-905c-78249e6aba06_2559x140.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_!hSo8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc485f339-d696-448b-905c-78249e6aba06_2559x140.png 424w, https://substackcdn.com/image/fetch/$s_!hSo8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc485f339-d696-448b-905c-78249e6aba06_2559x140.png 848w, https://substackcdn.com/image/fetch/$s_!hSo8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc485f339-d696-448b-905c-78249e6aba06_2559x140.png 1272w, https://substackcdn.com/image/fetch/$s_!hSo8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc485f339-d696-448b-905c-78249e6aba06_2559x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>A team says, &#8220;This is eight points.&#8221; Management hears, &#8220;This is twice as expensive as a four.&#8221; Another team has a different scale, so someone proposes normalization. Soon the organization starts comparing velocities across teams, as if points were a universal currency rather than a local thinking tool.</p><p>Then velocity becomes a target. The target becomes pressure. Pressure changes behavior. Teams inflate points, split work awkwardly, protect their numbers, avoid hard problems, or optimize for ticket throughput rather than outcomes for users.</p><p>This is how a coordination tool becomes a control tool. The moment story points become a way to compare teams, punish forecast misses, or promise delivery dates, they have left the realm of planning and entered the realm of theater.</p><p>The organization is no longer asking what is the most valuable thing it can finish next. It is asking how to make the numbers look predictable enough to keep the machine calm.</p><p>That is not agility. That is bureaucracy with poker cards.</p><p><a href="https://ronjeffries.com/articles/019-01ff/story-points/Index.html">https://ronjeffries.com/articles/019-01ff/story-points/Index.html</a></p><h2>Push: visibility through pressure</h2><p>Estimate culture often hides a push system.</p><p>A push system begins outside the real capacity of the team. Work is pushed in because someone wants it started, because someone wants people busy, because a date was promised, or because a plan says ten things should be in motion. The system is judged by visible activity: full calendars, loaded sprints, many initiatives, many tickets, many status updates.</p><p>From the outside, this can look responsible. Inside the system, it often creates congestion.</p><p>Push is not only too much work. Push is a management model where desire enters the system before capacity, evidence, and learning. Dates are pushed. Scope is pushed. Commitments are pushed. Work is pushed. The team is then asked to absorb the contradiction.</p><p>Push also tries to solve the visibility problem through pressure. The organization wants to see progress, but instead of making progress observable, it forces more work into plans, more dates into roadmaps, and more commitments into reporting. This produces visibility of intention, not visibility of reality.</p><p>Keeping everyone busy can destroy real productivity because a system full of activity can still be empty of results. When work is pushed into the system faster than it can flow through it, tasks compete for attention, queues grow, coordination cost rises, feedback arrives late, and the customer receives less.</p><p>Estimation can feed this trap. If management sees estimates as promises, then every planning cycle becomes an opportunity to push more work into the system. If the team says &#8220;this much,&#8221; someone asks for more. If the team delivers, the baseline rises. If the team misses, the answer is not to inspect flow, reduce batch size, or remove dependencies. The answer is to estimate better.</p><p>This creates a toxic loop.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Bu6g!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1927763-d40e-4c92-afee-3d3c88702518_717x1788.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Bu6g!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1927763-d40e-4c92-afee-3d3c88702518_717x1788.png 424w, https://substackcdn.com/image/fetch/$s_!Bu6g!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1927763-d40e-4c92-afee-3d3c88702518_717x1788.png 848w, https://substackcdn.com/image/fetch/$s_!Bu6g!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1927763-d40e-4c92-afee-3d3c88702518_717x1788.png 1272w, https://substackcdn.com/image/fetch/$s_!Bu6g!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1927763-d40e-4c92-afee-3d3c88702518_717x1788.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Bu6g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1927763-d40e-4c92-afee-3d3c88702518_717x1788.png" width="717" height="1788" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a1927763-d40e-4c92-afee-3d3c88702518_717x1788.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1788,&quot;width&quot;:717,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:96911,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1927763-d40e-4c92-afee-3d3c88702518_717x1788.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_!Bu6g!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1927763-d40e-4c92-afee-3d3c88702518_717x1788.png 424w, https://substackcdn.com/image/fetch/$s_!Bu6g!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1927763-d40e-4c92-afee-3d3c88702518_717x1788.png 848w, https://substackcdn.com/image/fetch/$s_!Bu6g!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1927763-d40e-4c92-afee-3d3c88702518_717x1788.png 1272w, https://substackcdn.com/image/fetch/$s_!Bu6g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1927763-d40e-4c92-afee-3d3c88702518_717x1788.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The loop survives because each step feels locally reasonable. Stakeholders need visibility. Managers need coordination. Teams need plans. The issue is not that any single person is irrational. The issue is that the system converts a legitimate need into a mechanism that makes the need harder to satisfy.</p><p>The more the organization pushes for certainty, the less predictable the system becomes.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1nis!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26068372-8dca-4a9f-bf40-c9efd1a3ce7d_631x1180.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1nis!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26068372-8dca-4a9f-bf40-c9efd1a3ce7d_631x1180.png 424w, https://substackcdn.com/image/fetch/$s_!1nis!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26068372-8dca-4a9f-bf40-c9efd1a3ce7d_631x1180.png 848w, https://substackcdn.com/image/fetch/$s_!1nis!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26068372-8dca-4a9f-bf40-c9efd1a3ce7d_631x1180.png 1272w, https://substackcdn.com/image/fetch/$s_!1nis!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26068372-8dca-4a9f-bf40-c9efd1a3ce7d_631x1180.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1nis!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26068372-8dca-4a9f-bf40-c9efd1a3ce7d_631x1180.png" width="631" height="1180" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/26068372-8dca-4a9f-bf40-c9efd1a3ce7d_631x1180.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1180,&quot;width&quot;:631,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:54279,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26068372-8dca-4a9f-bf40-c9efd1a3ce7d_631x1180.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_!1nis!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26068372-8dca-4a9f-bf40-c9efd1a3ce7d_631x1180.png 424w, https://substackcdn.com/image/fetch/$s_!1nis!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26068372-8dca-4a9f-bf40-c9efd1a3ce7d_631x1180.png 848w, https://substackcdn.com/image/fetch/$s_!1nis!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26068372-8dca-4a9f-bf40-c9efd1a3ce7d_631x1180.png 1272w, https://substackcdn.com/image/fetch/$s_!1nis!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26068372-8dca-4a9f-bf40-c9efd1a3ce7d_631x1180.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Pull: visibility through finished work</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!U_sh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68d2d7a1-9de9-4e42-825c-3ce3d34c2077_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!U_sh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68d2d7a1-9de9-4e42-825c-3ce3d34c2077_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!U_sh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68d2d7a1-9de9-4e42-825c-3ce3d34c2077_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!U_sh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68d2d7a1-9de9-4e42-825c-3ce3d34c2077_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!U_sh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68d2d7a1-9de9-4e42-825c-3ce3d34c2077_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!U_sh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68d2d7a1-9de9-4e42-825c-3ce3d34c2077_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/68d2d7a1-9de9-4e42-825c-3ce3d34c2077_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1045463,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68d2d7a1-9de9-4e42-825c-3ce3d34c2077_1024x559.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_!U_sh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68d2d7a1-9de9-4e42-825c-3ce3d34c2077_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!U_sh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68d2d7a1-9de9-4e42-825c-3ce3d34c2077_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!U_sh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68d2d7a1-9de9-4e42-825c-3ce3d34c2077_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!U_sh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68d2d7a1-9de9-4e42-825c-3ce3d34c2077_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A pull system starts from a different question. Not &#8220;How much can we push into the team?&#8221; but &#8220;What can the system finish responsibly, expose clearly, and learn from next?&#8221;</p><p>Pull does not mean passivity. It does not mean waiting around. It means work enters the system according to real capacity, explicit policies, and the ability to finish. It means done matters more than started. It means progress is not a percentage in a tool, but a change in reality.</p><p>Pull creates visibility through finished work. Push tries to create visibility by forcing work into a plan. That contrast matters because it changes what the organization treats as evidence.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gii-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c82cf1a-7849-42b3-a397-66673e1ef359_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gii-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c82cf1a-7849-42b3-a397-66673e1ef359_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!gii-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c82cf1a-7849-42b3-a397-66673e1ef359_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!gii-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c82cf1a-7849-42b3-a397-66673e1ef359_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!gii-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c82cf1a-7849-42b3-a397-66673e1ef359_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gii-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c82cf1a-7849-42b3-a397-66673e1ef359_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6c82cf1a-7849-42b3-a397-66673e1ef359_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:623026,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c82cf1a-7849-42b3-a397-66673e1ef359_1024x558.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_!gii-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c82cf1a-7849-42b3-a397-66673e1ef359_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!gii-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c82cf1a-7849-42b3-a397-66673e1ef359_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!gii-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c82cf1a-7849-42b3-a397-66673e1ef359_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!gii-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c82cf1a-7849-42b3-a397-66673e1ef359_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In a push model, someone outside the system often defines the commitment: the date, the scope, the pressure, the expectation. The team is then forced to reconcile the plan with reality. In a pull model, the team makes capacity and constraints visible, finishes work according to a clear definition of done, and collaborates with stakeholders on what should be pulled next.</p><p>Empirical product work follows a simple idea: knowledge comes from experience, and decisions should be based on what is observed. In complex environments, the future cannot be known with precision, so forward-looking decisions need evidence, feedback, and adaptation.</p><p>That does not mean &#8220;stop forecasting.&#8221; It means forecasts should be grounded in reality, not fantasy. It also means that the useful unit of progress is not a promise. The useful unit of progress is something that can be inspected: a usable increment, a working slice, a released capability, a validated assumption, or a visible reduction of risk.</p><p>The same principle can exist inside or outside a named framework. The practice is not the ceremony. The principle is that progress must become observable soon enough to steer.</p><p>Sometimes that observable progress is a formal review. Sometimes it is a customer conversation. Sometimes it is a small release. Sometimes it is a prototype in the hands of users. Sometimes it is a narrow production slice behind a feature flag. Sometimes it is learning that proves an idea should stop.</p><p>Pull is not only a delivery mechanism. It is a governance model. It asks the organization to decide from finished work, customer exposure, flow evidence, and learning. It forces respect for the difference between work that has been started, work that has been finished, and work that has changed something valuable.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KjaF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e2520a8-891d-4723-8f66-df25b33157d5_2941x188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KjaF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e2520a8-891d-4723-8f66-df25b33157d5_2941x188.png 424w, https://substackcdn.com/image/fetch/$s_!KjaF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e2520a8-891d-4723-8f66-df25b33157d5_2941x188.png 848w, https://substackcdn.com/image/fetch/$s_!KjaF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e2520a8-891d-4723-8f66-df25b33157d5_2941x188.png 1272w, https://substackcdn.com/image/fetch/$s_!KjaF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e2520a8-891d-4723-8f66-df25b33157d5_2941x188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KjaF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e2520a8-891d-4723-8f66-df25b33157d5_2941x188.png" width="1456" height="93" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9e2520a8-891d-4723-8f66-df25b33157d5_2941x188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:93,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:37407,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e2520a8-891d-4723-8f66-df25b33157d5_2941x188.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_!KjaF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e2520a8-891d-4723-8f66-df25b33157d5_2941x188.png 424w, https://substackcdn.com/image/fetch/$s_!KjaF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e2520a8-891d-4723-8f66-df25b33157d5_2941x188.png 848w, https://substackcdn.com/image/fetch/$s_!KjaF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e2520a8-891d-4723-8f66-df25b33157d5_2941x188.png 1272w, https://substackcdn.com/image/fetch/$s_!KjaF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e2520a8-891d-4723-8f66-df25b33157d5_2941x188.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Public progress, private forecasts</h2><p>A healthier organization does not need to ban every internal forecast. Teams can reason privately about size, uncertainty, risk, sequencing, and capacity. That thinking can be useful, especially when it helps the team choose a safer path, slice the work better, or decide whether a larger bet deserves attention.</p><p>The problem begins when internal uncertainty tools become public promises.</p><p>Private forecasts can support thinking. Public progress should support visibility.</p><p>That distinction changes the conversation. The team can still ask, &#8220;How large does this feel?&#8221;, &#8220;What do we not know yet?&#8221;, &#8220;What similar work have we done before?&#8221;, &#8220;What could make this take much longer?&#8221;, and &#8220;What is the smallest slice that would reduce uncertainty?&#8221; Those questions help the team reason.</p><p>But people outside the team should not have to trust a hidden internal number as proof of progress. They should see reality. They should see finished slices, released increments, validated assumptions, customer reactions, flow evidence, and explicit risk.</p><p>This does not mean every piece of work needs a formal presentation. Public progress is not a ceremony. It is the practice of making reality visible to the people who need to decide.</p><p>A working slice can be public progress. A production release can be public progress. A user validating a partial behavior can be public progress. A customer saying, &#8220;This is not solving the problem,&#8221; can be public progress if it prevents a larger waste. A technical spike that reveals a dangerous dependency can be public progress if it changes the investment decision.</p><p>The key is not whether the organization has a demo. The key is whether the organization has evidence.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SScF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F911b969f-686c-497a-8b42-4b0fabeef64b_3267x290.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SScF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F911b969f-686c-497a-8b42-4b0fabeef64b_3267x290.png 424w, https://substackcdn.com/image/fetch/$s_!SScF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F911b969f-686c-497a-8b42-4b0fabeef64b_3267x290.png 848w, https://substackcdn.com/image/fetch/$s_!SScF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F911b969f-686c-497a-8b42-4b0fabeef64b_3267x290.png 1272w, https://substackcdn.com/image/fetch/$s_!SScF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F911b969f-686c-497a-8b42-4b0fabeef64b_3267x290.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SScF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F911b969f-686c-497a-8b42-4b0fabeef64b_3267x290.png" width="1456" height="129" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/911b969f-686c-497a-8b42-4b0fabeef64b_3267x290.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:129,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:58466,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F911b969f-686c-497a-8b42-4b0fabeef64b_3267x290.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_!SScF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F911b969f-686c-497a-8b42-4b0fabeef64b_3267x290.png 424w, https://substackcdn.com/image/fetch/$s_!SScF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F911b969f-686c-497a-8b42-4b0fabeef64b_3267x290.png 848w, https://substackcdn.com/image/fetch/$s_!SScF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F911b969f-686c-497a-8b42-4b0fabeef64b_3267x290.png 1272w, https://substackcdn.com/image/fetch/$s_!SScF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F911b969f-686c-497a-8b42-4b0fabeef64b_3267x290.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Output, outcome, and throughput as a better language</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wwGA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cc5bdd-8d26-4032-a9f4-82470bf072c1_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wwGA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cc5bdd-8d26-4032-a9f4-82470bf072c1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!wwGA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cc5bdd-8d26-4032-a9f4-82470bf072c1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!wwGA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cc5bdd-8d26-4032-a9f4-82470bf072c1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!wwGA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cc5bdd-8d26-4032-a9f4-82470bf072c1_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wwGA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cc5bdd-8d26-4032-a9f4-82470bf072c1_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/73cc5bdd-8d26-4032-a9f4-82470bf072c1_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:923517,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cc5bdd-8d26-4032-a9f4-82470bf072c1_1024x559.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_!wwGA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cc5bdd-8d26-4032-a9f4-82470bf072c1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!wwGA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cc5bdd-8d26-4032-a9f4-82470bf072c1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!wwGA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cc5bdd-8d26-4032-a9f4-82470bf072c1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!wwGA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73cc5bdd-8d26-4032-a9f4-82470bf072c1_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>One reason organizations lean on estimates is that they lack a better vocabulary for progress. They use dates because they do not know how else to talk about delivery. They use velocity because they do not know how else to talk about capacity. They use roadmap completion because they do not know how else to talk about value.</p><p>This is where the distinction between output, outcome, and throughput helps.</p><p>Output is what the team ships: features, releases, integrations, fixes, experiments, flows, migrations. It is real, visible, and necessary, but it does not prove value by itself.</p><p>Outcome is what changes because of the work: user behavior, customer success, operational improvement, business performance, risk reduction, or learning.</p><p>Throughput is how much meaningful work the system finishes over time.</p><p>These three ideas are related, but they are not equivalent. That distinction matters because estimate culture often collapses everything into one question: &#8220;When will it be done?&#8221;</p><p>A healthier organization asks several better questions.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7T_e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F500e4f18-6a24-4ee1-9cf3-9bc8f417dce7_1651x537.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7T_e!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F500e4f18-6a24-4ee1-9cf3-9bc8f417dce7_1651x537.png 424w, https://substackcdn.com/image/fetch/$s_!7T_e!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F500e4f18-6a24-4ee1-9cf3-9bc8f417dce7_1651x537.png 848w, https://substackcdn.com/image/fetch/$s_!7T_e!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F500e4f18-6a24-4ee1-9cf3-9bc8f417dce7_1651x537.png 1272w, https://substackcdn.com/image/fetch/$s_!7T_e!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F500e4f18-6a24-4ee1-9cf3-9bc8f417dce7_1651x537.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7T_e!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F500e4f18-6a24-4ee1-9cf3-9bc8f417dce7_1651x537.png" width="1651" height="537" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/500e4f18-6a24-4ee1-9cf3-9bc8f417dce7_1651x537.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:537,&quot;width&quot;:1651,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:110729,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6a52360-ecd1-485f-8416-a14f5aed6c5c_1720x664.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_!7T_e!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F500e4f18-6a24-4ee1-9cf3-9bc8f417dce7_1651x537.png 424w, https://substackcdn.com/image/fetch/$s_!7T_e!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F500e4f18-6a24-4ee1-9cf3-9bc8f417dce7_1651x537.png 848w, https://substackcdn.com/image/fetch/$s_!7T_e!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F500e4f18-6a24-4ee1-9cf3-9bc8f417dce7_1651x537.png 1272w, https://substackcdn.com/image/fetch/$s_!7T_e!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F500e4f18-6a24-4ee1-9cf3-9bc8f417dce7_1651x537.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Throughput is especially important because slow systems delay learning. If work reaches users late, feedback arrives late. If feedback arrives late, product decisions remain speculative for longer. A team can have excellent intentions and still lose economically because its learning loop is too slow.</p><p>A flow-based system focuses on how value moves through the organization: how much work is in progress, how long work waits, how often work finishes, and where it gets stuck. Flow metrics such as <em>work in progress</em> (WIP), cycle time, work item age, and throughput are useful because they describe the system from evidence.</p><p>They do not remove uncertainty, but they make the conversation more honest. If work item age keeps growing, something is stuck. If <em>work in progress</em> (WIP) grows, the system is taking on more than it finishes. If throughput is unstable, forecasts should be treated carefully. If cycle time is rising, delivery is becoming slower no matter how busy everyone looks.</p><p>This is the difference between steering and pretending. Estimation theater asks the team to predict the river. Flow-based management asks where the water is actually blocked.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Aksa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45e00f-4534-42bd-8fc3-e92741988f96_2273x396.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Aksa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45e00f-4534-42bd-8fc3-e92741988f96_2273x396.png 424w, https://substackcdn.com/image/fetch/$s_!Aksa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45e00f-4534-42bd-8fc3-e92741988f96_2273x396.png 848w, https://substackcdn.com/image/fetch/$s_!Aksa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45e00f-4534-42bd-8fc3-e92741988f96_2273x396.png 1272w, https://substackcdn.com/image/fetch/$s_!Aksa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45e00f-4534-42bd-8fc3-e92741988f96_2273x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Aksa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45e00f-4534-42bd-8fc3-e92741988f96_2273x396.png" width="1456" height="254" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ca45e00f-4534-42bd-8fc3-e92741988f96_2273x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:254,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:63137,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45e00f-4534-42bd-8fc3-e92741988f96_2273x396.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_!Aksa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45e00f-4534-42bd-8fc3-e92741988f96_2273x396.png 424w, https://substackcdn.com/image/fetch/$s_!Aksa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45e00f-4534-42bd-8fc3-e92741988f96_2273x396.png 848w, https://substackcdn.com/image/fetch/$s_!Aksa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45e00f-4534-42bd-8fc3-e92741988f96_2273x396.png 1272w, https://substackcdn.com/image/fetch/$s_!Aksa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45e00f-4534-42bd-8fc3-e92741988f96_2273x396.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;21f765f2-49f5-4b10-8b61-7ad31310f235&quot;,&quot;caption&quot;:&quot;&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Output, Outcome, and Throughput: What Product Teams are really measuring when they say they deliver value&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-05-12T05:01:35.830Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/vimeo/w_728,c_limit,d_video_placeholder.png/206617354&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/output-outcome-and-throughput-what&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:189660243,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>The steering loop</h2><p>The alternative to estimate-driven control is not one big heroic release. It is a loop.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Az2s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2b5c0f-f6e8-48c8-88e5-70b7028a49b2_1024x1900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Az2s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2b5c0f-f6e8-48c8-88e5-70b7028a49b2_1024x1900.png 424w, https://substackcdn.com/image/fetch/$s_!Az2s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2b5c0f-f6e8-48c8-88e5-70b7028a49b2_1024x1900.png 848w, https://substackcdn.com/image/fetch/$s_!Az2s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2b5c0f-f6e8-48c8-88e5-70b7028a49b2_1024x1900.png 1272w, https://substackcdn.com/image/fetch/$s_!Az2s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2b5c0f-f6e8-48c8-88e5-70b7028a49b2_1024x1900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Az2s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2b5c0f-f6e8-48c8-88e5-70b7028a49b2_1024x1900.png" width="1024" height="1900" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8a2b5c0f-f6e8-48c8-88e5-70b7028a49b2_1024x1900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1900,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:115933,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2b5c0f-f6e8-48c8-88e5-70b7028a49b2_1024x1900.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_!Az2s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2b5c0f-f6e8-48c8-88e5-70b7028a49b2_1024x1900.png 424w, https://substackcdn.com/image/fetch/$s_!Az2s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2b5c0f-f6e8-48c8-88e5-70b7028a49b2_1024x1900.png 848w, https://substackcdn.com/image/fetch/$s_!Az2s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2b5c0f-f6e8-48c8-88e5-70b7028a49b2_1024x1900.png 1272w, https://substackcdn.com/image/fetch/$s_!Az2s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a2b5c0f-f6e8-48c8-88e5-70b7028a49b2_1024x1900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This loop changes the emotional structure of the organization. Instead of asking teams to defend predictions, it asks everyone to inspect evidence. Instead of treating change as failure, it treats learning as the point. Instead of hiding uncertainty until the deadline, it brings uncertainty into the conversation while the organization can still act.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wWGP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733bc0c9-d059-4a81-8251-b4420e310673_861x1793.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wWGP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733bc0c9-d059-4a81-8251-b4420e310673_861x1793.png 424w, https://substackcdn.com/image/fetch/$s_!wWGP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733bc0c9-d059-4a81-8251-b4420e310673_861x1793.png 848w, https://substackcdn.com/image/fetch/$s_!wWGP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733bc0c9-d059-4a81-8251-b4420e310673_861x1793.png 1272w, https://substackcdn.com/image/fetch/$s_!wWGP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733bc0c9-d059-4a81-8251-b4420e310673_861x1793.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wWGP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733bc0c9-d059-4a81-8251-b4420e310673_861x1793.png" width="861" height="1793" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/733bc0c9-d059-4a81-8251-b4420e310673_861x1793.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1793,&quot;width&quot;:861,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:93016,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733bc0c9-d059-4a81-8251-b4420e310673_861x1793.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_!wWGP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733bc0c9-d059-4a81-8251-b4420e310673_861x1793.png 424w, https://substackcdn.com/image/fetch/$s_!wWGP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733bc0c9-d059-4a81-8251-b4420e310673_861x1793.png 848w, https://substackcdn.com/image/fetch/$s_!wWGP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733bc0c9-d059-4a81-8251-b4420e310673_861x1793.png 1272w, https://substackcdn.com/image/fetch/$s_!wWGP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733bc0c9-d059-4a81-8251-b4420e310673_861x1793.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>This is harder than demanding a date. It requires smaller slices, clearer definitions of done, technical discipline, customer access, honest conversations, and management willing to redirect investment when evidence changes.</p><p>That is the part many organizations underestimate. Estimation theater is often a shortcut around the discipline required to create real visibility. It is easier to ask for a number than to create a system that frequently finishes small things, exposes them honestly, learns from users, limits <em>work in progress</em> (WIP), protects quality, and adapts from evidence.</p><p>The shortcut is tempting because it feels cheaper, but the cost usually reappears later as rework, delayed feedback, overloaded teams, weaker quality, and decisions made from fiction instead of evidence.</p><h2>A practical migration path</h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5mw8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27c243bc-004b-4cf0-a579-ae493b3be6bc_3652x236.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5mw8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27c243bc-004b-4cf0-a579-ae493b3be6bc_3652x236.png 424w, https://substackcdn.com/image/fetch/$s_!5mw8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27c243bc-004b-4cf0-a579-ae493b3be6bc_3652x236.png 848w, https://substackcdn.com/image/fetch/$s_!5mw8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27c243bc-004b-4cf0-a579-ae493b3be6bc_3652x236.png 1272w, https://substackcdn.com/image/fetch/$s_!5mw8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27c243bc-004b-4cf0-a579-ae493b3be6bc_3652x236.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5mw8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27c243bc-004b-4cf0-a579-ae493b3be6bc_3652x236.png" width="1456" height="94" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/27c243bc-004b-4cf0-a579-ae493b3be6bc_3652x236.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:94,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:65858,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27c243bc-004b-4cf0-a579-ae493b3be6bc_3652x236.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_!5mw8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27c243bc-004b-4cf0-a579-ae493b3be6bc_3652x236.png 424w, https://substackcdn.com/image/fetch/$s_!5mw8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27c243bc-004b-4cf0-a579-ae493b3be6bc_3652x236.png 848w, https://substackcdn.com/image/fetch/$s_!5mw8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27c243bc-004b-4cf0-a579-ae493b3be6bc_3652x236.png 1272w, https://substackcdn.com/image/fetch/$s_!5mw8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27c243bc-004b-4cf0-a579-ae493b3be6bc_3652x236.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>A team that wants less estimation theater needs more than a slogan. It needs replacement practices. Otherwise the organization will return to estimates because estimates, however weak, at least feel like a mechanism.</p><p>The migration should not begin with &#8220;we no longer estimate.&#8221; That can create unnecessary resistance, especially in organizations where people are already anxious about visibility. The better move is to demote the estimate and strengthen the system around it.</p><p>The first step is to name the decision before estimating. The organization should ask what decision will change because of the number. Will it start, stop, defer, reduce scope, increase investment, choose a different option, or manage a real external constraint? If no decision changes, the estimate should not be produced by default.</p><p>The second step is to separate forecast from commitment. A forecast is a projection under uncertainty. A commitment is a promise made with enough knowledge, agency, and responsibility. Mixing them creates counterfeit commitment.</p><p>The third step is to attach dates at the right level. A date may be useful when uncertainty is low enough, the constraint is real, and the trade-offs are explicit. A date attached too early, at the wrong level, with the wrong confidence, creates fake certainty.</p><p>The fourth step is to begin with the investment question. Instead of asking, &#8220;How long will all of this take?&#8221;, ask, &#8220;How much are we willing to invest before we need stronger evidence?&#8221; This reframes the work as an economic decision, not a fortune-telling exercise.</p><p>The fifth step is to slice until progress can be exposed. A slice should reduce uncertainty, deliver a small capability, test an assumption, or make a real conversation possible. Large work is not only harder to estimate. It is harder to learn from.</p><p>The sixth step is to define done explicitly. Done should not mean &#8220;coded on my machine&#8221; or &#8220;merged but not usable.&#8221; Done should mean the work meets the quality measures required for the product and is integrated enough to inspect honestly.</p><p>The seventh step is to expose progress to the right people. Show customers, users, stakeholders, support, operations, or sales what matters when it matters. This does not need to be a formal demo. It needs to be real enough to improve the conversation.</p><p>The eighth step is to measure flow without worshiping metrics. Track <em>work in progress</em> (WIP), throughput, cycle time, and work item age to understand the system. Do not convert these metrics into another punishment mechanism. Metrics should reveal constraints, not create fear.</p><p>The ninth step is to discuss output, outcome, and throughput separately. Do not say &#8220;we delivered value&#8221; just because something shipped. Ask what was delivered, what changed, and whether the system is improving its ability to finish meaningful work.</p><p>The tenth step is to use forecasts only with assumptions and ranges. When forecasting is necessary, use historical data, confidence language, explicit assumptions, and conditions that would invalidate the forecast. A single date without uncertainty is usually a political artifact, not a forecast.</p><p>The final step is to review investment continuously. The key question is not whether the original estimate was correct. The key question is whether continuing is still the best use of attention, money, and opportunity.</p><p>This is how an organization moves from prediction to steering. Not by deleting planning, but by making planning more honest.</p><h2>When forecasting can still help</h2><p>A serious article against estimation theater must make one distinction clear: forecasting is not the same as estimation theater.</p><p>Forecasting can help when it supports a real decision. Estimation theater does not. Forecasting uses evidence, assumptions, ranges, and uncertainty to decide what to do. Estimation theater scores work, creates numbers, defends dates, and often turns uncertainty into pressure.</p><p>That distinction matters.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RTzb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e65237-986f-49b2-87bf-3951c3bbb0b8_1661x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RTzb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e65237-986f-49b2-87bf-3951c3bbb0b8_1661x768.png 424w, https://substackcdn.com/image/fetch/$s_!RTzb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e65237-986f-49b2-87bf-3951c3bbb0b8_1661x768.png 848w, https://substackcdn.com/image/fetch/$s_!RTzb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e65237-986f-49b2-87bf-3951c3bbb0b8_1661x768.png 1272w, https://substackcdn.com/image/fetch/$s_!RTzb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e65237-986f-49b2-87bf-3951c3bbb0b8_1661x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RTzb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e65237-986f-49b2-87bf-3951c3bbb0b8_1661x768.png" width="1661" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d4e65237-986f-49b2-87bf-3951c3bbb0b8_1661x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1661,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:165020,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bc3aeaa-058e-437e-a247-bfc87e66ec24_1720x904.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_!RTzb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e65237-986f-49b2-87bf-3951c3bbb0b8_1661x768.png 424w, https://substackcdn.com/image/fetch/$s_!RTzb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e65237-986f-49b2-87bf-3951c3bbb0b8_1661x768.png 848w, https://substackcdn.com/image/fetch/$s_!RTzb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e65237-986f-49b2-87bf-3951c3bbb0b8_1661x768.png 1272w, https://substackcdn.com/image/fetch/$s_!RTzb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4e65237-986f-49b2-87bf-3951c3bbb0b8_1661x768.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Forecasting can be useful when there is a real external constraint. It can be useful for portfolio decisions. It can be useful when comparing options. It can be useful when deciding whether a product bet is economically plausible. It can be useful when a team needs to reason about capacity for a short planning horizon. It can be useful when the organization has historical data and uses it with explicit uncertainty.</p><p>The issue is not thinking about the future. The issue is pretending that a number has removed uncertainty.</p><p>Software estimation research gives us a reason to be humble. Effort estimates are often fragile, overruns are common, and confidence is frequently stronger than the evidence deserves. That does not mean forecasting is impossible. It means organizations should stop acting as if the first estimate deserves the emotional status of truth.</p><p>Better forecasting practices tend to respect the outside view. Instead of relying only on internal optimism, they look at comparable past work, actual cycle times, historical throughput, delivery patterns, and previous overruns. If similar initiatives have always taken longer than expected, the current initiative should not be treated as magically different because the slide deck is cleaner.</p><p>A useful forecast should come with:</p><ul><li><p>The decision it supports</p></li><li><p>The assumptions behind it</p></li><li><p>The historical data used</p></li><li><p>The range of plausible outcomes</p></li><li><p>The confidence level or uncertainty</p></li><li><p>The conditions that would invalidate it</p></li><li><p>The next checkpoint where the forecast will be revised<br>If that feels too heavy for the decision, the estimate may not be worth producing.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DTXP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c3cf63-314a-43e1-b993-6ae8abb71a3d_2330x696.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DTXP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c3cf63-314a-43e1-b993-6ae8abb71a3d_2330x696.png 424w, https://substackcdn.com/image/fetch/$s_!DTXP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c3cf63-314a-43e1-b993-6ae8abb71a3d_2330x696.png 848w, https://substackcdn.com/image/fetch/$s_!DTXP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c3cf63-314a-43e1-b993-6ae8abb71a3d_2330x696.png 1272w, https://substackcdn.com/image/fetch/$s_!DTXP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c3cf63-314a-43e1-b993-6ae8abb71a3d_2330x696.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DTXP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c3cf63-314a-43e1-b993-6ae8abb71a3d_2330x696.png" width="1456" height="435" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/68c3cf63-314a-43e1-b993-6ae8abb71a3d_2330x696.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:435,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:81458,&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://emmanuelvalverderamos.substack.com/i/200102391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c3cf63-314a-43e1-b993-6ae8abb71a3d_2330x696.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_!DTXP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c3cf63-314a-43e1-b993-6ae8abb71a3d_2330x696.png 424w, https://substackcdn.com/image/fetch/$s_!DTXP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c3cf63-314a-43e1-b993-6ae8abb71a3d_2330x696.png 848w, https://substackcdn.com/image/fetch/$s_!DTXP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c3cf63-314a-43e1-b993-6ae8abb71a3d_2330x696.png 1272w, https://substackcdn.com/image/fetch/$s_!DTXP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68c3cf63-314a-43e1-b993-6ae8abb71a3d_2330x696.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Conclusions</h2><p>Estimating is not automatically evil, but estimation theater is harmful. Forecasting can help when it supports a real decision, carries uncertainty honestly, and remains subordinate to investment judgment. It becomes a con when it produces prediction for nothing, satisfies self-inflicted dates, creates counterfeit commitment, or replaces visibility, collaboration, strategy, roadmap, trust, and steering.</p><p>Direction must come before dates, and evidence must come before confidence. The organization should not let estimates create the timeline, then let the timeline pretend to be the roadmap, then let the roadmap pretend to be strategy. Outputs are bets made in service of direction. <em>Objectives and Key Results</em> (OKRs), <em>Key Results</em> (KRs), and <em>Key Performance Indicators</em> (KPIs) matter because they help the organization inspect whether those bets are changing anything important.</p><p>The score is not the valuable part. The valuable work is reducing uncertainty. If estimation creates a useful conversation, keep the conversation and remove the weaker artifact. Talk about uncertainty, slicing, dependencies, implementation options, risk, and user value. But do not pretend that assigning a number is what creates understanding.</p><p>I do not consider estimation a valuable capability to invest in. Whatever local calibration a team may gain is too fragile, too context-dependent, and too hard to transfer. The same task changes when the team, codebase, domain, architecture, tests, dependencies, and organizational constraints change. The better investment is not to become better at guessing complexity. The better investment is to reduce complexity.</p><p>If the goal is complexity, attack it. If the goal is visibility, expose progress. If the goal is strategy, define direction. Make the work smaller, make the behavior clearer, make the risk visible, make the next step safer, and make progress observable.</p><p>The estimate is not the enemy. The lie is the enemy. And the lie begins when uncertainty is dressed as certainty, sold as control, and pushed into a team as if pressure could make the future obey.</p><p>I&#8217;ve created an app to explain this post: <a href="https://how-software-works.netlify.app/">https://how-software-works.netlify.app/ </a></p><h2>References</h2><ul><li><p>Beck, Kent, Beedle, Mike, van Bennekum, Arie, Cockburn, Alistair, Cunningham, Ward, Fowler, Martin, Grenning, James, Highsmith, Jim, Hunt, Andrew, Jeffries, Ron, Kern, Jon, Marick, Brian, Martin, Robert C., Mellor, Steve, Schwaber, Ken, Sutherland, Jeff, Thomas, Dave. <em>Manifesto for Agile Software Development</em>. </p></li></ul><p>https://agilemanifesto.org/</p><ul><li><p>Beck, Kent. <em>Private Estimates, Public Progress</em>. </p></li></ul><div class="embedded-post-wrap" data-attrs="{&quot;id&quot;:138262656,&quot;url&quot;:&quot;https://tidyfirst.substack.com/p/private-estimates-public-progress&quot;,&quot;publication_id&quot;:256838,&quot;embedding_publication_id&quot;:null,&quot;publication_name&quot;:&quot;Software Design: Tidy First?&quot;,&quot;publication_logo_url&quot;:null,&quot;title&quot;:&quot;Private Estimates, Public Progress&quot;,&quot;truncated_body_text&quot;:&quot;I remember back when being on one of those gigantic, long-lived software projects when I was a wee programmer. Professional project managers had laid out the entire thing before we started coding. Enlightened professional project managers&#8212;they only assumed 4.2 hours of coding per programmer per day.&quot;,&quot;date&quot;:&quot;2023-10-25T15:25:16.952Z&quot;,&quot;like_count&quot;:98,&quot;comment_count&quot;:18,&quot;bylines&quot;:[{&quot;id&quot;:24333739,&quot;name&quot;:&quot;Kent Beck&quot;,&quot;handle&quot;:&quot;kentbeck&quot;,&quot;previous_name&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F000da410-0ed6-4a25-80b1-6a46e964ae0b_242x242.jpeg&quot;,&quot;bio&quot;:&quot;Programmer, artist, coach coach, singer/guitarist, peripatetic. Learning to be me. Full-time content producer. Mailto:kentlbeck@gmail.com&quot;,&quot;profile_set_up_at&quot;:&quot;2021-04-20T15:03:12.374Z&quot;,&quot;reader_installed_at&quot;:&quot;2022-03-09T14:46:10.113Z&quot;,&quot;publicationUsers&quot;:[{&quot;id&quot;:226404,&quot;user_id&quot;:24333739,&quot;publication_id&quot;:256838,&quot;role&quot;:&quot;admin&quot;,&quot;public&quot;:true,&quot;is_primary&quot;:true,&quot;publication&quot;:{&quot;id&quot;:256838,&quot;name&quot;:&quot;Software Design: Tidy First?&quot;,&quot;subdomain&quot;:&quot;tidyfirst&quot;,&quot;custom_domain&quot;:null,&quot;custom_domain_optional&quot;:false,&quot;hero_text&quot;:&quot;Software design is an exercise in human relationships. So are all the other techniques we use to develop software. How can we geeks get better at technique as one way of getting better at relationships?&quot;,&quot;logo_url&quot;:null,&quot;author_id&quot;:24333739,&quot;primary_user_id&quot;:24333739,&quot;theme_var_background_pop&quot;:&quot;#FD5353&quot;,&quot;created_at&quot;:&quot;2021-01-07T21:22:19.269Z&quot;,&quot;email_from_name&quot;:null,&quot;copyright&quot;:&quot;Kent Beck&quot;,&quot;founding_plan_name&quot;:&quot;Founding Member&quot;,&quot;community_enabled&quot;:true,&quot;invite_only&quot;:false,&quot;payments_state&quot;:&quot;enabled&quot;,&quot;language&quot;:null,&quot;explicit&quot;:false,&quot;homepage_type&quot;:&quot;magaziney&quot;,&quot;is_personal_mode&quot;:false,&quot;logo_url_wide&quot;:null}},{&quot;id&quot;:199084,&quot;user_id&quot;:24333739,&quot;publication_id&quot;:285700,&quot;role&quot;:&quot;admin&quot;,&quot;public&quot;:true,&quot;is_primary&quot;:false,&quot;publication&quot;:{&quot;id&quot;:285700,&quot;name&quot;:&quot;Geek Incentives&quot;,&quot;subdomain&quot;:&quot;geekincentives&quot;,&quot;custom_domain&quot;:null,&quot;custom_domain_optional&quot;:false,&quot;hero_text&quot;:&quot;Creating and maintaining incentives that help geeks do their best work&quot;,&quot;logo_url&quot;:null,&quot;author_id&quot;:24333739,&quot;primary_user_id&quot;:null,&quot;theme_var_background_pop&quot;:&quot;#2EE240&quot;,&quot;created_at&quot;:&quot;2021-02-13T15:28:41.577Z&quot;,&quot;email_from_name&quot;:null,&quot;copyright&quot;:&quot;Kent Beck&quot;,&quot;founding_plan_name&quot;:null,&quot;community_enabled&quot;:true,&quot;invite_only&quot;:false,&quot;payments_state&quot;:&quot;disabled&quot;,&quot;language&quot;:null,&quot;explicit&quot;:false,&quot;homepage_type&quot;:null,&quot;is_personal_mode&quot;:false,&quot;logo_url_wide&quot;:null}},{&quot;id&quot;:718034,&quot;user_id&quot;:24333739,&quot;publication_id&quot;:780969,&quot;role&quot;:&quot;admin&quot;,&quot;public&quot;:true,&quot;is_primary&quot;:false,&quot;publication&quot;:{&quot;id&quot;:780969,&quot;name&quot;:&quot;Kent Beck Poker&quot;,&quot;subdomain&quot;:&quot;kentbeckpoker&quot;,&quot;custom_domain&quot;:null,&quot;custom_domain_optional&quot;:false,&quot;hero_text&quot;:&quot;Watch as I learn to play poker, warts and all&quot;,&quot;logo_url&quot;:null,&quot;author_id&quot;:24333739,&quot;primary_user_id&quot;:null,&quot;theme_var_background_pop&quot;:&quot;#6C0095&quot;,&quot;created_at&quot;:&quot;2022-03-02T01:27:36.844Z&quot;,&quot;email_from_name&quot;:null,&quot;copyright&quot;:&quot;Kent Beck&quot;,&quot;founding_plan_name&quot;:null,&quot;community_enabled&quot;:true,&quot;invite_only&quot;:false,&quot;payments_state&quot;:&quot;disabled&quot;,&quot;language&quot;:null,&quot;explicit&quot;:false,&quot;homepage_type&quot;:null,&quot;is_personal_mode&quot;:false,&quot;logo_url_wide&quot;:null}},{&quot;id&quot;:3026154,&quot;user_id&quot;:24333739,&quot;publication_id&quot;:2975268,&quot;role&quot;:&quot;admin&quot;,&quot;public&quot;:true,&quot;is_primary&quot;:false,&quot;publication&quot;:{&quot;id&quot;:2975268,&quot;name&quot;:&quot;Leading Geeks with Purpose&quot;,&quot;subdomain&quot;:&quot;leadgeekswithpurpose&quot;,&quot;custom_domain&quot;:null,&quot;custom_domain_optional&quot;:false,&quot;hero_text&quot;:&quot;Leading Geeks with Purpose: The Brass Ring Approach is a one-day workshop for non-technical executives who want to get the full returns from their technology investment.&quot;,&quot;logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F000da410-0ed6-4a25-80b1-6a46e964ae0b_242x242.jpeg&quot;,&quot;author_id&quot;:24333739,&quot;primary_user_id&quot;:null,&quot;theme_var_background_pop&quot;:&quot;#FF6719&quot;,&quot;created_at&quot;:&quot;2024-09-04T14:16:12.791Z&quot;,&quot;email_from_name&quot;:null,&quot;copyright&quot;:&quot;Kent Beck&quot;,&quot;founding_plan_name&quot;:null,&quot;community_enabled&quot;:true,&quot;invite_only&quot;:false,&quot;payments_state&quot;:&quot;disabled&quot;,&quot;language&quot;:null,&quot;explicit&quot;:false,&quot;homepage_type&quot;:&quot;newspaper&quot;,&quot;is_personal_mode&quot;:false,&quot;logo_url_wide&quot;:null}},{&quot;id&quot;:9137860,&quot;user_id&quot;:24333739,&quot;publication_id&quot;:8915297,&quot;role&quot;:&quot;admin&quot;,&quot;public&quot;:true,&quot;is_primary&quot;:false,&quot;publication&quot;:{&quot;id&quot;:8915297,&quot;name&quot;:&quot;Kent Says Fuck Parkinson's&quot;,&quot;subdomain&quot;:&quot;fparkinsons&quot;,&quot;custom_domain&quot;:null,&quot;custom_domain_optional&quot;:false,&quot;hero_text&quot;:&quot;Having been recently diagnosed with Parkinson's, I'm here to think about how I'm going to deal &amp; cope. Whining, raging, cogitating, questioning. Never medical advice.&quot;,&quot;logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F000da410-0ed6-4a25-80b1-6a46e964ae0b_242x242.jpeg&quot;,&quot;author_id&quot;:24333739,&quot;primary_user_id&quot;:null,&quot;theme_var_background_pop&quot;:&quot;#FF6719&quot;,&quot;created_at&quot;:&quot;2026-05-04T15:46:51.255Z&quot;,&quot;email_from_name&quot;:null,&quot;copyright&quot;:&quot;Kent Beck&quot;,&quot;founding_plan_name&quot;:null,&quot;community_enabled&quot;:true,&quot;invite_only&quot;:false,&quot;payments_state&quot;:&quot;disabled&quot;,&quot;language&quot;:null,&quot;explicit&quot;:false,&quot;homepage_type&quot;:&quot;newspaper&quot;,&quot;is_personal_mode&quot;:false,&quot;logo_url_wide&quot;:null}}],&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100,&quot;status&quot;:{&quot;bestsellerTier&quot;:100,&quot;subscriberTier&quot;:5,&quot;leaderboard&quot;:null,&quot;vip&quot;:false,&quot;badge&quot;:{&quot;type&quot;:&quot;bestseller&quot;,&quot;tier&quot;:100},&quot;paidPublicationIds&quot;:[365497,2527260,458709,1154379,1441644,313411,3748372,9711,35345],&quot;subscriber&quot;:null}}],&quot;utm_campaign&quot;:null,&quot;belowTheFold&quot;:true,&quot;type&quot;:&quot;newsletter&quot;,&quot;language&quot;:&quot;en&quot;,&quot;source&quot;:null}" data-component-name="EmbeddedPostToDOM"><a class="embedded-post" native="true" href="https://tidyfirst.substack.com/p/private-estimates-public-progress?utm_source=substack&amp;utm_campaign=post_embed&amp;utm_medium=web"><div class="embedded-post-header"><span></span><span class="embedded-post-publication-name">Software Design: Tidy First?</span></div><div class="embedded-post-title-wrapper"><div class="embedded-post-title">Private Estimates, Public Progress</div></div><div class="embedded-post-body">I remember back when being on one of those gigantic, long-lived software projects when I was a wee programmer. Professional project managers had laid out the entire thing before we started coding. Enlightened professional project managers&#8212;they only assumed 4.2 hours of coding per programmer per day&#8230;</div><div class="embedded-post-cta-wrapper"><span class="embedded-post-cta">Read more</span></div><div class="embedded-post-meta">3 years ago &#183; 98 likes &#183; 18 comments &#183; Kent Beck</div></a></div><ul><li><p>Coleman, John, Vacanti, Daniel, Johnson, Colleen, Singh, Prateek, Wester, Julia, Neverdal, Christian, Firlit, Magdalena, Gilb, Tom, Tendon, Steve. <em>The Kanban Guide</em>. <a href="https://kanbanguides.org/the-kanban-guide/">https://kanbanguides.org/the-kanban-guide/</a></p></li><li><p>Flyvbjerg, Bent. <em>Curbing Optimism Bias and Strategic Misrepresentation in Planning: Reference Class Forecasting in Practice</em>. <a href="https://ideas.repec.org/a/taf/eurpls/v16y2006i1p3-21.html">https://ideas.repec.org/a/taf/eurpls/v16y2006i1p3-21.html</a></p></li><li><p>Garz&#225;s, Javier. <em>Cazadores de desperdicios... &#191;estimar es un desperdicio?</em> <a href="https://javiergarzas.com/2019/02/cazadores-de-desperdicios-estimar-es-un-desperdicio.html">https://javiergarzas.com/2019/02/cazadores-de-desperdicios-estimar-es-un-desperdicio.html</a></p></li><li><p>Garz&#225;s, Javier. <em>&#191;Qu&#233; es un desperdicio?</em> <a href="https://javiergarzas.com/2020/06/que-es-un-desperdicio.html">https://javiergarzas.com/2020/06/que-es-un-desperdicio.html</a></p></li><li><p>Jeffries, Ron. <em>Estimation is Evil</em>. <a href="https://ronjeffries.com/articles/021-01ff/estimation-is-evil/">https://ronjeffries.com/articles/021-01ff/estimation-is-evil/</a></p></li><li><p>Jeffries, Ron. <em>Story Points Revisited</em>. <a href="https://ronjeffries.com/articles/019-01ff/story-points/Index.html">https://ronjeffries.com/articles/019-01ff/story-points/Index.html</a></p></li><li><p>J&#248;rgensen, Magne. <em>What We Do and Don&#8217;t Know about Software Development Effort Estimation</em>. <a href="https://www.infoq.com/articles/software-development-effort-estimation/">https://www.infoq.com/articles/software-development-effort-estimation/</a></p></li><li><p>Kerievsky, Joshua. <em>The Heart of Agile Begins</em>. <a href="https://heartofagile.com/the-heart-of-agile-begins/">https://heartofagile.com/the-heart-of-agile-begins/</a></p></li><li><p>Schwaber, Ken, Sutherland, Jeff. <em>The 2020 Scrum Guide</em>. <a href="https://scrumguides.org/scrum-guide.html">https://scrumguides.org/scrum-guide.html</a></p></li><li><p>Valverde Ramos, Emmanuel. <em>Output, Outcome, and Throughput: What Product Teams are really measuring when they say they deliver value</em>. <a href="https://emmanuelvalverderamos.substack.com/p/output-outcome-and-throughput-what">https://emmanuelvalverderamos.substack.com/p/output-outcome-and-throughput-what</a></p></li><li><p>Valverde Ramos, Emmanuel. <em>Product Strategy is not a Roadmap</em>. <a href="https://emmanuelvalverderamos.substack.com/p/product-strategy-is-not-a-roadmap">https://emmanuelvalverderamos.substack.com/p/product-strategy-is-not-a-roadmap</a></p></li><li><p>Valverde Ramos, Emmanuel. <em>The Resource Utilization Trap: Why Keeping Everyone Busy Can Destroy Real Productivity</em>. <a href="https://emmanuelvalverderamos.substack.com/p/the-resource-utilization-trap-why">https://emmanuelvalverderamos.substack.com/p/the-resource-utilization-trap-why</a></p></li><li><p>Zuill, Woody. <em>Life, Liberty, and the Pursuit of Agility: Estimating</em>. <a href="https://zuill.us/WoodyZuill/category/estimating/">https://zuill.us/WoodyZuill/category/estimating/</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Ways of Working and Working Agreements: what they are, why they matter, and how to tell them apart]]></title><description><![CDATA[There is a common problem in teams and organisations that is rarely explained with enough clarity: people often talk about improving the way they work, but they do not always mean the same thing.]]></description><link>https://emmanuelvalverderamos.substack.com/p/ways-of-working-and-working-agreements</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/ways-of-working-and-working-agreements</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 26 May 2026 06:36:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!rVG_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rVG_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rVG_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!rVG_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!rVG_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!rVG_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rVG_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2191566,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/189655599?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.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_!rVG_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!rVG_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!rVG_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!rVG_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bfcff57-fb82-4111-b8fe-030dbed915c1_1408x752.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There is a common problem in teams and organisations that is rarely explained with enough clarity: people often talk about improving the way they work, but they do not always mean the same thing.</p><p>Sometimes they are talking about planning, decision-making, priorities, quality standards, or coordination across teams. At other times, they are talking about response times, meeting behaviour, feedback, or how to handle disagreements. These topics are related, but they do not belong to exactly the same level of team life.</p><p>This is where two important ideas come in: <strong>ways of working</strong> and <strong>working agreements</strong>.</p><p>These expressions are often used as if they meant the same thing. In practice, however, they refer to different layers of how a team operates. Understanding that difference matters. Without it, teams tend to mix structural issues with behavioural ones, and that usually leads to weak diagnosis, vague conversations, and poor improvement efforts.</p><p>This article explains what <strong>ways of working</strong> are, what <strong>working agreements</strong> are, and why it is useful to distinguish clearly between them.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>What are ways of working?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oPOD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7eb148-bda4-4cdb-924e-b0058bd4c7e1_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oPOD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7eb148-bda4-4cdb-924e-b0058bd4c7e1_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!oPOD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7eb148-bda4-4cdb-924e-b0058bd4c7e1_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!oPOD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7eb148-bda4-4cdb-924e-b0058bd4c7e1_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!oPOD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7eb148-bda4-4cdb-924e-b0058bd4c7e1_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oPOD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7eb148-bda4-4cdb-924e-b0058bd4c7e1_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2f7eb148-bda4-4cdb-924e-b0058bd4c7e1_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2033570,&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://emmanuelvalverderamos.substack.com/i/189655599?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7eb148-bda4-4cdb-924e-b0058bd4c7e1_1408x752.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_!oPOD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7eb148-bda4-4cdb-924e-b0058bd4c7e1_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!oPOD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7eb148-bda4-4cdb-924e-b0058bd4c7e1_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!oPOD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7eb148-bda4-4cdb-924e-b0058bd4c7e1_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!oPOD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f7eb148-bda4-4cdb-924e-b0058bd4c7e1_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The expression <strong>ways of working</strong> refers to the shared framework that defines <strong>how work happens</strong> in a team, a project, or an organisation.</p><p>This is an important point: ways of working are not mainly about <strong>what</strong> a team delivers. They are about <strong>how</strong> the team organises, coordinates, executes, reviews, and improves that work over time.</p><p>In other words, ways of working describe the operating logic of a team.</p><p>They include the principles, practices, standards, rhythms, responsibilities, and mechanisms that make work possible in a consistent and understandable way. They shape how tasks move from idea to execution, how decisions are made, how quality is maintained, and how learning is built into the system.</p><p>A team&#8217;s ways of working may include questions such as these:</p><ul><li><p>How is work planned?</p></li><li><p>How is work prioritised?</p></li><li><p>How are responsibilities distributed?</p></li><li><p>How are decisions made?</p></li><li><p>How is progress tracked?</p></li><li><p>How are dependencies managed?</p></li><li><p>How is quality protected?</p></li><li><p>How does the team review and improve its own system?</p></li></ul><p>Seen from this perspective, ways of working are not a collection of isolated habits. They are the broader structure that allows a team to function with coherence.</p><h3>Why ways of working matter</h3><p>Every team has ways of working, even when nobody has written them down.</p><p>If they are not explicit, they still exist in practice. They appear in routines, assumptions, informal rules, and repeated behaviours. The problem is that unspoken ways of working often create confusion. People follow different expectations, responsibilities become blurred, and decisions depend too much on personal interpretation.</p><p>Making ways of working explicit helps a team create a shared understanding of how work is meant to flow. That does not remove complexity, but it makes complexity easier to manage.</p><p>Well-defined ways of working help teams:</p><ul><li><p>Reduce ambiguity</p></li><li><p>Align expectations</p></li><li><p>Improve coordination</p></li><li><p>Create more consistent quality</p></li><li><p>Make work more visible</p></li><li><p>Onboard new people more effectively and build a stronger basis for continuous improvement.</p></li></ul><h3>What kinds of elements belong to ways of working?</h3><p>Because ways of working describe the operating framework of the team, they usually include structural and systemic elements.</p><p>These may include:</p><ul><li><p>The overall model of work, such as working in cycles or in continuous flow</p></li><li><p>Planning and review rhythms</p></li><li><p>Prioritisation criteria</p></li><li><p>Roles and responsibilities</p></li><li><p>Rules for escalation and decision-making</p></li><li><p>Quality expectations</p></li><li><p>How work is visualised and tracked</p></li><li><p>How handovers or dependencies are managed and the criteria that define when work is ready to begin and when it can be considered complete.</p></li></ul><p>This last point is especially important. Criteria for readiness and completion are not just casual preferences. They are part of the way the work system protects quality and consistency. For that reason, they belong more naturally to ways of working than to team behaviour alone.</p><h3>A simple example</h3><p>Imagine a product team that says the following:</p><ul><li><p>Work is planned every Monday</p></li><li><p>Priorities are reviewed weekly</p></li><li><p>Tasks are visible on a shared board</p></li><li><p>Important changes require review before release</p></li><li><p>Progress is checked at the end of each week and the team reflects regularly on how to improve its system.</p></li></ul><p>These are not random customs. Together, they describe the team&#8217;s way of operating. That is what ways of working are: the broader pattern that gives shape to how work happens.</p><h2>What are working agreements?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4Mpi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd171d282-173a-4a00-898e-13a12f606c89_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4Mpi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd171d282-173a-4a00-898e-13a12f606c89_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!4Mpi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd171d282-173a-4a00-898e-13a12f606c89_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!4Mpi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd171d282-173a-4a00-898e-13a12f606c89_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!4Mpi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd171d282-173a-4a00-898e-13a12f606c89_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4Mpi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd171d282-173a-4a00-898e-13a12f606c89_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d171d282-173a-4a00-898e-13a12f606c89_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1854452,&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://emmanuelvalverderamos.substack.com/i/189655599?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd171d282-173a-4a00-898e-13a12f606c89_1408x752.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_!4Mpi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd171d282-173a-4a00-898e-13a12f606c89_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!4Mpi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd171d282-173a-4a00-898e-13a12f606c89_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!4Mpi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd171d282-173a-4a00-898e-13a12f606c89_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!4Mpi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd171d282-173a-4a00-898e-13a12f606c89_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If ways of working define how the work system operates, <strong>working agreements</strong> define how the people inside that system agree to collaborate day by day.</p><p>A working agreement is an explicit agreement within a team about how people will communicate, coordinate, support one another, and behave while doing the work.</p><p>This is a narrower concept than ways of working, but it is no less important.</p><p>Where ways of working describe the broader operating framework, working agreements focus on the practical and relational layer of teamwork. They make expectations visible in places where confusion tends to arise: response times, meetings, feedback, interruptions, escalation, requests for help, review practices, and collaboration norms.</p><p>Working agreements answer questions such as:</p><ul><li><p>How quickly do we respond to internal messages?</p></li><li><p>How do we signal urgency?</p></li><li><p>What do we do when work becomes blocked?</p></li><li><p>How do we behave in meetings?</p></li><li><p>How do we give feedback?</p></li><li><p>How do we handle disagreement?</p></li><li><p>How do we review each other&#8217;s work?</p></li><li><p>How do we coordinate around sensitive technical changes?</p></li></ul><p>In that sense, working agreements do not define the entire work system. They define the agreed behaviours and interaction rules that help people function well inside that system.</p><h3>Why working agreements matter</h3><p>Teams often assume that collaboration will take care of itself. It rarely does.</p><p>Without explicit agreements, people tend to work from personal habits. One person may expect fast replies, another may value uninterrupted focus. One person may see direct feedback as helpful, while another may experience it as abrupt. One person may escalate quickly, while another may wait too long.</p><p>None of these tensions necessarily come from bad intent. More often, they come from unspoken expectations.</p><p>Working agreements reduce that problem by turning assumptions into shared commitments.</p><p>They help teams:</p><ul><li><p>Clarify expectations</p></li><li><p>Reduce avoidable friction</p></li><li><p>Improve communication</p></li><li><p>Make collaboration more predictable</p></li><li><p>Strengthen accountability and create healthier working relationships.</p></li></ul><p>They are especially valuable in teams that are cross-functional, distributed, fast-growing, or under pressure, because in those environments the cost of ambiguity rises quickly.</p><h3>Working agreements are not only about behaviour</h3><p>A common mistake is to think that working agreements only concern soft topics such as tone, manners, or meeting etiquette.</p><p>They can include those things, but they can also include technical agreements, as long as the purpose is to align how people work together.</p><p>For example, a team may agree:</p><ul><li><p>That certain changes require an additional review</p></li><li><p>That specific checks must be completed before integration</p></li><li><p>That technical decisions affecting others must be documented or that urgent incidents must be communicated through a specific channel.</p></li></ul><p>These are technical in content, but collaborative in function. They regulate how people coordinate around technical work.</p><p>That is why working agreements should not be reduced to rules of politeness. They are better understood as explicit agreements about day-to-day collaboration, including relational, operational, and technical aspects where relevant.</p><h3>A simple example</h3><p>Imagine that the same product team also agrees on the following:</p><ul><li><p>Internal messages should be answered within one working day</p></li><li><p>Blocked work should be raised on the same day</p></li><li><p>Meetings begin on time</p></li><li><p>Feedback should focus on the work, not on the person</p></li><li><p>Major technical changes require an additional review and decisions made in meetings must be written down afterwards.</p></li></ul><p>These agreements do not define the whole work system. They define how people act within it. That is the role of working agreements.</p><h2>The relationship between ways of working and working agreements</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sqTD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9137bf24-82d3-4901-8112-f9e6c2a8310a_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sqTD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9137bf24-82d3-4901-8112-f9e6c2a8310a_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!sqTD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9137bf24-82d3-4901-8112-f9e6c2a8310a_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!sqTD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9137bf24-82d3-4901-8112-f9e6c2a8310a_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!sqTD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9137bf24-82d3-4901-8112-f9e6c2a8310a_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sqTD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9137bf24-82d3-4901-8112-f9e6c2a8310a_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9137bf24-82d3-4901-8112-f9e6c2a8310a_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2505876,&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://emmanuelvalverderamos.substack.com/i/189655599?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9137bf24-82d3-4901-8112-f9e6c2a8310a_1408x752.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_!sqTD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9137bf24-82d3-4901-8112-f9e6c2a8310a_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!sqTD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9137bf24-82d3-4901-8112-f9e6c2a8310a_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!sqTD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9137bf24-82d3-4901-8112-f9e6c2a8310a_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!sqTD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9137bf24-82d3-4901-8112-f9e6c2a8310a_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>At this point, the connection between the two concepts becomes clearer.</p><p>Ways of working and working agreements are related, but they are not interchangeable.</p><p>The simplest way to understand the relationship is this:</p><ul><li><p><strong>Ways of working</strong> define how work functions as a system.</p></li><li><p><strong>Working agreements</strong> define how people collaborate within that system.</p></li></ul><p>This means that working agreements often sit inside the wider frame created by ways of working. They help make the operating model workable in everyday practice.</p><p>If ways of working provide the architecture of work, working agreements provide some of the social and practical rules that allow people to move inside that architecture effectively.</p><p>This distinction matters because not every problem belongs to the same layer.</p><p>If a team struggles with unclear priorities, weak flow, or inconsistent quality standards, the issue may belong to ways of working.</p><p>If a team struggles with poor feedback, delayed responses, unclear meeting behaviour, or lack of coordination, the issue may belong to working agreements.</p><p>When teams mix these layers, they often solve the wrong problem.</p><h2>The key differences between ways of working and working agreements</h2><p>The difference between the two concepts becomes easier to see when they are compared directly.</p><h3>Scope</h3><p>Ways of working have a broader scope. They describe the overall framework through which work is organised and delivered.</p><p>Working agreements have a narrower scope. They describe specific agreements about how people interact and collaborate within that framework.</p><h3>Focus</h3><p>Ways of working focus on the operation of the work system: planning, flow, priorities, decision-making, quality, visibility, and improvement.</p><p>Working agreements focus on the day-to-day dynamics of collaboration: communication, coordination, meetings, feedback, escalation, and shared expectations.</p><h3>Nature</h3><p>Ways of working are structural and systemic.</p><p>Working agreements are practical and relational, although they can also include technical agreements when those agreements regulate collaboration.</p><h3>Purpose</h3><p>Ways of working create consistency in how work is organised and executed.</p><p>Working agreements create clarity in how people work together while that work is being organised and executed.</p><h3>Typical examples</h3><p>Examples of ways of working include:</p><ul><li><p>Plnning work in regular cycles</p></li><li><p>Using a shared board to manage flow</p></li><li><p>Prioritising by impact and urgency</p></li><li><p>Defining clear quality standards</p></li><li><p>Establishing criteria for when work can start and establishing criteria for when work can be considered complete.</p></li></ul><p>Examples of working agreements include:</p><ul><li><p>Answering internal messages within an agreed time</p></li><li><p>Raising blockers on the same day</p></li><li><p>Beginning meetings on time</p></li><li><p>Giving feedback on the work rather than on the individual</p></li><li><p>Documenting decisions after discussions and requesting additional review for sensitive changes.</p></li></ul><h2>A useful test: how to know which one you are looking at</h2><p>When it is not obvious whether something belongs to ways of working or working agreements, one simple test can help.</p><p>Ask this question:</p><p><strong>Is this mainly about how the work system functions, or is it mainly about how people coordinate within that system?</strong></p><p>If it is mainly about how work flows, how decisions are structured, how quality is protected, or how the operating model is designed, it is probably part of ways of working.</p><p>If it is mainly about how people communicate, collaborate, respond, review, help, or behave in practice, it is probably a working agreement.</p><p>This test is not perfect in every edge case, but it is strong enough to create much more clarity than treating both ideas as the same thing.</p><h2>Why the distinction is worth making</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ju9H!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d273ea7-81fa-46e6-b275-f402fa148fd0_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ju9H!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d273ea7-81fa-46e6-b275-f402fa148fd0_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!ju9H!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d273ea7-81fa-46e6-b275-f402fa148fd0_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!ju9H!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d273ea7-81fa-46e6-b275-f402fa148fd0_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!ju9H!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d273ea7-81fa-46e6-b275-f402fa148fd0_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ju9H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d273ea7-81fa-46e6-b275-f402fa148fd0_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7d273ea7-81fa-46e6-b275-f402fa148fd0_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2926282,&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://emmanuelvalverderamos.substack.com/i/189655599?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d273ea7-81fa-46e6-b275-f402fa148fd0_1408x752.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_!ju9H!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d273ea7-81fa-46e6-b275-f402fa148fd0_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!ju9H!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d273ea7-81fa-46e6-b275-f402fa148fd0_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!ju9H!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d273ea7-81fa-46e6-b275-f402fa148fd0_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!ju9H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d273ea7-81fa-46e6-b275-f402fa148fd0_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It can be tempting to dismiss this distinction as a matter of terminology. It is not.</p><p>The words teams use shape the way they diagnose problems and design improvements.</p><p>If everything is treated as one blurred category, conversations become vague. Teams say they need to &#8220;improve how we work,&#8221; but they do not know whether they need to redesign their operating model or simply clarify their agreements with one another.</p><p>A team that does not distinguish between these levels may try to fix a structural problem with a behavioural rule, or try to solve a collaboration issue by changing a process that was not the real cause.</p><p>That is why the distinction matters. It supports better thinking, better language, and better intervention.</p><p>It helps teams ask sharper questions:</p><ul><li><p>Is this a system issue or a collaboration issue?</p></li><li><p>Do we need to redesign how work flows, or do we need to clarify expectations between people?</p></li><li><p>Are we changing the operating model, or are we updating our agreements about daily interaction?</p></li></ul><p>Those are not minor differences. They are the basis of more rigorous team improvement.</p><h2>Final reflection</h2><p>Every team works in two ways at the same time.</p><p>It works through a system: priorities, rhythms, roles, standards, decisions, and flow.</p><p>It also works through relationships: communication, trust, coordination, feedback, and shared habits.</p><p>Ways of working help define the first. Working agreements help shape the second.</p><p>Both matter. Neither replaces the other. And the better a team understands the difference, the more effectively it can improve not only what it does, but how it does it together.</p><h2>Closing definition</h2><p>Ways of working are the shared framework that defines how work is organised, executed, and improved. Working agreements are the explicit agreements that define how people communicate, coordinate, and collaborate within that framework.</p><p>Understanding the difference between them is not an academic exercise. It is a practical step towards clearer teamwork, better decisions, and more intentional organisations.</p>]]></content:encoded></item><item><title><![CDATA[AI does not replace XP or Agile. It raises the cost of pretending.]]></title><description><![CDATA[AI can make teams more productive at producing code, and less effective at delivering value.]]></description><link>https://emmanuelvalverderamos.substack.com/p/ai-does-not-replace-xp-or-agile-it</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/ai-does-not-replace-xp-or-agile-it</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Thu, 21 May 2026 06:01:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!5mzI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5mzI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5mzI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!5mzI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!5mzI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!5mzI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5mzI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:868461,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.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_!5mzI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!5mzI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!5mzI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!5mzI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dd57499-c0bb-4764-9f50-9356fc17b9f2_1024x558.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>An airport installs new self-check-in kiosks.</p><p>The machines are fast. Passengers print boarding passes in seconds. The queue at the entrance almost disappears. Managers look at the dashboard and celebrate: more passengers processed per minute, fewer staff needed at the counter, less waiting at the first visible step.</p><p>Then the rest of the airport starts to bend. Security cannot absorb the new flow. Bags arrive at the wrong belt. Boarding gates fill too early. More passengers reach the gate before the aircraft is ready. Some miss flights because the bottleneck did not disappear. It moved.</p><p>From the passenger&#8217;s point of view, the airport did not become better. They do not care how quickly a boarding pass was printed if the plane leaves late, the bag is lost, or the connection is missed. The machine made one part of the system faster. It did not make the journey better.</p><p>Much of the current conversation about <em>Artificial Intelligence</em> (<em>AI</em>) and software development confuses a faster station with a better journey.</p><p>Coding agents can produce code, tests, refactors, documentation, migrations, and pull requests faster than many teams were used to. That matters. It changes the economics of code production. It changes the shape of daily work. It changes what is cheap to try.</p><p>But it does not change what software is accountable for.</p><p>Users still care whether the product helps them, whether existing behaviour works after a release, whether the system is available when needed, whether incidents are resolved quickly, and whether each change increases or decreases their trust.</p><p>The business still cares whether software creates value, reduces risk, preserves optionality, improves time to market, and remains economically possible to own. The system still cares whether changes are coherent, tested, integrated, observable, recoverable, maintainable, and aligned with reality.</p><p>None of that disappears because the code came from a coding agent.</p><p>That is why the claim that <em>AI</em> replaces <em>Extreme Programming</em> (<em>XP</em>) or Agile is backwards. <em>AI</em> does not make those disciplines less relevant. It makes the absence of those disciplines more expensive.</p><h2>TL;DR</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Fnls!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98dfb6f9-0b40-4e08-bed0-c3ce65c82e23_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Fnls!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98dfb6f9-0b40-4e08-bed0-c3ce65c82e23_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Fnls!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98dfb6f9-0b40-4e08-bed0-c3ce65c82e23_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Fnls!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98dfb6f9-0b40-4e08-bed0-c3ce65c82e23_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Fnls!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98dfb6f9-0b40-4e08-bed0-c3ce65c82e23_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Fnls!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98dfb6f9-0b40-4e08-bed0-c3ce65c82e23_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/98dfb6f9-0b40-4e08-bed0-c3ce65c82e23_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:684926,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98dfb6f9-0b40-4e08-bed0-c3ce65c82e23_1024x559.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_!Fnls!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98dfb6f9-0b40-4e08-bed0-c3ce65c82e23_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Fnls!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98dfb6f9-0b40-4e08-bed0-c3ce65c82e23_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Fnls!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98dfb6f9-0b40-4e08-bed0-c3ce65c82e23_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Fnls!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98dfb6f9-0b40-4e08-bed0-c3ce65c82e23_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p><em>AI</em> coding agents can increase code production. They can also improve local efficiency by reducing the effort required to produce certain artefacts. But software development is judged by effectiveness: whether a team turns ideas into valuable, reliable, working software in users&#8217; hands, while preserving the ability to change the system again tomorrow.</p></li><li><p>Code was not the final output before <em>AI</em>, and it is not the final output now. The output that matters is value.</p></li><li><p>If a team generates one hundred times more code but delivers value later, breaks existing functionality more often, increases review load, or takes longer to recover from incidents, the system did not improve. It became busier.</p></li><li><p>That distinction explains why the evidence around <em>AI</em> looks mixed. A controlled GitHub Copilot study found that developers completed a bounded JavaScript task 55.8% faster with Copilot. METR&#8217;s 2025 randomized controlled trial found that experienced open-source developers working on familiar repositories took 19% longer when <em>AI</em> tools were allowed. DORA 2025 frames <em>AI</em> as an amplifier of organisational strengths and weaknesses, not as a substitute for the underlying system. Faros&#8217; 2026 telemetry reports higher throughput signals alongside larger pull requests, more bugs per pull request, longer review time, more incidents per pull request, and more code churn.</p></li><li><p>Agile and <em>XP</em> were not meant to be ceremonies. Agile points toward early and continuous delivery of valuable software, working software, collaboration, technical excellence, simplicity, and adaptation. <em>XP</em> gives those ideas sharper engineering teeth through short cycles, automated tests, <em>Test-Driven Development</em> (<em>TDD</em>), refactoring, incremental design, small releases, shared ownership, and frequent integration.</p></li><li><p>The better question is not: &#8220;Can <em>AI</em> write the code?&#8221; It can.</p></li><li><p>The better question is: &#8220;What keeps generated change aligned with user value, system behaviour, design integrity, and operational reality once it enters a living system?&#8221; That question brings Agile and <em>XP</em> back into focus.</p></li></ul><p></p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Index</h2><ol><li><p>Code was never the outcome</p></li><li><p>Productivity, efficiency, and effectiveness are not the same thing</p></li><li><p><em>AI</em> made one station faster, not the whole system</p></li><li><p>Output is not effectiveness</p></li><li><p>The user does not care who wrote the code</p></li><li><p>The story of a cheap change</p></li><li><p><em>AI</em> does not replace Agile. It exposes fake Agile</p></li><li><p><em>AI</em> does not replace <em>XP</em>. It exposes weak feedback</p></li><li><p><em>Test-Driven Development</em> (<em>TDD</em>) as executable intent</p></li><li><p><em>Trunk-Based Development</em> as integration discipline</p></li><li><p><em>Continuous Delivery</em> as value flow</p></li><li><p>Refactoring as economic control</p></li><li><p>Small batches as protection against generated inventory</p></li><li><p>Pairing, review, and shared judgment</p></li><li><p>&#8220;Use <em>AI</em>&#8221; is not a strategy</p></li><li><p>What teams should measure now</p></li><li><p>Conclusions</p></li><li><p>References</p></li></ol><h2>Code was never the outcome</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-z89!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f754e42-001b-4e30-94b0-40e577767380_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-z89!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f754e42-001b-4e30-94b0-40e577767380_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!-z89!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f754e42-001b-4e30-94b0-40e577767380_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!-z89!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f754e42-001b-4e30-94b0-40e577767380_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!-z89!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f754e42-001b-4e30-94b0-40e577767380_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-z89!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f754e42-001b-4e30-94b0-40e577767380_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f754e42-001b-4e30-94b0-40e577767380_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:561682,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f754e42-001b-4e30-94b0-40e577767380_1024x558.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_!-z89!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f754e42-001b-4e30-94b0-40e577767380_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!-z89!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f754e42-001b-4e30-94b0-40e577767380_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!-z89!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f754e42-001b-4e30-94b0-40e577767380_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!-z89!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f754e42-001b-4e30-94b0-40e577767380_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A team can generate a thousand lines of code before lunch and still be no closer to improving a user&#8217;s life. It can close tickets, merge pull requests, update dashboards, and produce beautiful internal reports while the product becomes slower, less reliable, harder to change, and more confusing to operate.</p><p>This was true before <em>AI</em>. <em>AI</em> has made the truth harder to ignore.</p><p>Software development has always suffered from a dangerous substitution: treating code as the output because code is visible, countable, reviewable, and easy to attach to individual activity. Code feels like progress because it is concrete. It appears in a repository. It moves through a pull request. It can be counted by tools that have no understanding of whether the change should exist.</p><p>But code is not value by itself.</p><p>Code is a liability until it earns its keep. It consumes attention. It needs to be understood, tested, secured, deployed, observed, documented, maintained, adapted, and eventually removed. It can create revenue, reduce cost, enable learning, improve user experience, and open future options. It can also increase basal cost, create cognitive drag, slow future change, expand the blast radius of incidents, and make the next valuable decision harder.</p><p>That is why cheap code is not the same as cheap software. Lowering the cost of creation does not remove the cost of ownership. It can increase the temptation to create more than the system can economically carry.</p><p>This is the core economic trap in <em>AI</em>-assisted development. The easiest part to accelerate is not always the part limiting the system&#8217;s ability to create value. The cost was not only writing the code. The cost was carrying it, changing it, trusting it, operating it, explaining it, and recovering when it behaved differently from what people expected.</p><p>A user does not experience code production. The user experiences the product.</p><p>They experience whether the flow helps them complete a task. They experience whether payment works. They experience whether yesterday&#8217;s feature still works after today&#8217;s release. They experience whether the system is available when they need it. They experience whether support can explain what happened. They experience whether the team recovers quickly when something fails.</p><p>A user does not care that a team generated one hundred times more code if the feature arrives later. They do not care that a coding agent produced a clever implementation if every release breaks something they depend on. They do not care that development looked cheaper if incidents now take thirteen hours to resolve.</p><p>From the user&#8217;s side of the glass, only the outcome counts.</p><h2>Productivity, efficiency, and effectiveness are not the same thing</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jhTy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F068cb451-c28c-4e0f-8889-c6696649f16e_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jhTy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F068cb451-c28c-4e0f-8889-c6696649f16e_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!jhTy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F068cb451-c28c-4e0f-8889-c6696649f16e_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!jhTy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F068cb451-c28c-4e0f-8889-c6696649f16e_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!jhTy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F068cb451-c28c-4e0f-8889-c6696649f16e_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jhTy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F068cb451-c28c-4e0f-8889-c6696649f16e_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/068cb451-c28c-4e0f-8889-c6696649f16e_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:917928,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F068cb451-c28c-4e0f-8889-c6696649f16e_1024x558.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_!jhTy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F068cb451-c28c-4e0f-8889-c6696649f16e_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!jhTy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F068cb451-c28c-4e0f-8889-c6696649f16e_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!jhTy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F068cb451-c28c-4e0f-8889-c6696649f16e_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!jhTy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F068cb451-c28c-4e0f-8889-c6696649f16e_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A lot of the <em>AI</em> conversation becomes confused because it uses the word &#8220;productivity&#8221; as if it meant everything good.</p><p>It does not.</p><p>Productivity is about output per unit of input. In software, this might mean more code, more generated tests, more pull requests, more completed tickets, more documentation, or more artefacts per developer-hour.</p><p>Efficiency is about using fewer resources or less waste to produce that output. A developer may spend less time typing boilerplate. A team may spend less effort scaffolding repetitive code. A coding agent may reduce the human effort needed to produce a first draft.</p><p>Effectiveness is different. Effectiveness is about achieving the intended outcome. In software, that means delivering valuable, reliable, usable, maintainable software to users, while preserving the ability to change the system again tomorrow.</p><p>The trap is that <em>AI</em> can improve productivity and efficiency locally while damaging effectiveness systemically.</p><p>A team can produce more code and still deliver value later. It can type less and still spend more time reviewing, correcting, integrating, debugging, and explaining. It can generate more pull requests and still increase lead time. It can create more tests and still miss the behaviour that matters. It can ship more often and still reduce user trust if each release becomes less reliable.</p><p>This is the distinction the industry keeps blurring.</p><p>More output is not automatically better software. Less effort is not automatically better economics. Faster local production is not automatically better value flow.</p><p>The real question is not whether <em>AI</em> makes teams more productive at producing code. In many contexts, it can. The real question is whether that extra output improves delivery effectiveness, or merely increases the amount of work the system must verify, integrate, operate, and eventually pay for.</p><p><em>AI</em> can make teams more productive at producing code, and less effective at delivering value.</p><h2><em>AI</em> made one station faster, not the whole system</h2><p>The wrong question is: &#8220;Can <em>AI</em> write the code?&#8221;</p><p>Of course it can.</p><p>It can write plausible code. It can modify existing code. It can scaffold tests. It can propose refactorings. It can explain APIs. It can migrate syntax. It can generate examples. It can produce a full vertical slice faster than many teams could manually type the first draft a few years ago.</p><p>The important question is whether the resulting change deserves to enter the system. That means asking:</p><ol><li><p>Does it express the right product intent?</p></li><li><p>Does it preserve existing behaviour?</p></li><li><p>Does it fit the design?</p></li><li><p>Does it reduce or increase the future cost of change?</p></li><li><p>Does it make incidents easier or harder to diagnose?</p></li><li><p>Does it create more knowledge in the team, or move knowledge into a prompt transcript nobody will read again?</p></li><li><p>Does it make the system easier to evolve, or does it merely make the pull request look complete?<br><em>AI</em> changes how fast change can be proposed. It does not automatically improve how safely change can be accepted.</p></li></ol><p>A generated pull request is not value. It is a claim on future judgment. Someone, or some combination of people and tools, still has to decide whether the change is correct, valuable, coherent, reversible, observable, and worth carrying. The scarce resource is no longer typing. In many teams, the scarce resource is judgment.</p><p>And judgment is exactly where weak Agile and weak <em>XP</em> collapse.</p><p>A team that already had unclear product strategy will use <em>AI</em> to generate faster ambiguity. A team that already had poor test discipline will generate more code with weak behavioural protection. A team that already had slow review will feed the review queue faster. A team that already avoided refactoring will accumulate generated design damage faster. A team that already confused activity with progress will finally get activity at industrial scale.</p><p>That is not transformation. It is acceleration without steering.</p><h2>Output is not effectiveness</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mWGG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1941cb02-12b0-4490-9615-905bc01c7588_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mWGG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1941cb02-12b0-4490-9615-905bc01c7588_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!mWGG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1941cb02-12b0-4490-9615-905bc01c7588_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!mWGG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1941cb02-12b0-4490-9615-905bc01c7588_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!mWGG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1941cb02-12b0-4490-9615-905bc01c7588_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mWGG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1941cb02-12b0-4490-9615-905bc01c7588_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1941cb02-12b0-4490-9615-905bc01c7588_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:559036,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1941cb02-12b0-4490-9615-905bc01c7588_1024x558.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_!mWGG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1941cb02-12b0-4490-9615-905bc01c7588_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!mWGG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1941cb02-12b0-4490-9615-905bc01c7588_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!mWGG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1941cb02-12b0-4490-9615-905bc01c7588_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!mWGG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1941cb02-12b0-4490-9615-905bc01c7588_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The serious <em>AI</em> conversation is not whether coding agents can increase output. In many contexts, they can. A team can generate more code, draft more tests, produce more pull requests, and move faster through the visible act of implementation. That may be an increase in local productivity. It may even be an increase in local efficiency if less human effort is needed to produce the same kind of artefact.</p><p>But software development is not judged by output alone. It is judged by effectiveness: whether the team turns ideas into valuable, reliable, working software in users&#8217; hands, while preserving the ability to change the system again tomorrow.</p><p>That distinction explains why the evidence looks mixed.</p><p>The GitHub Copilot study by Peng, Kalliamvakou, Cihon, and Demirer found that developers with Copilot completed a bounded JavaScript HTTP server task 55.8% faster than the control group. That is evidence of improved local task throughput in a constrained setting. It does not prove improved delivery effectiveness across a living software system.</p><p>METR&#8217;s 2025 randomized controlled trial looked at experienced open-source developers working on real issues in mature repositories they already knew. In that setting, developers expected <em>AI</em> to help and later believed it had helped, but measured completion time was 19% slower when <em>AI</em> was allowed. That result does not prove <em>AI</em> slows all work. It shows that once context, existing design, review expectations, tests, documentation, and repository reality enter the picture, local generation speed is no longer the same thing as system effectiveness.</p><p>Faros&#8217; 2026 telemetry points in the same direction. It reports higher throughput signals alongside larger pull requests, more bugs per pull request, longer review time, more incidents per pull request, and more churn. That should be treated as vendor telemetry, not universal causal proof. But it is exactly the kind of signal we should expect when output increases faster than the delivery system can absorb it.</p><p>DORA&#8217;s work sharpens the point: <em>AI</em> is an amplifier of the system it enters. Strong systems can convert <em>AI</em> assistance into better outcomes. Weak systems can convert <em>AI</em> assistance into more noise, more queues, and more expensive rework.</p><p>The lesson is not &#8220;<em>AI</em> is good&#8221; or &#8220;<em>AI</em> is bad.&#8221; The lesson is that output, efficiency, and effectiveness are different things. <em>AI</em> can improve the first two locally while damaging the third systemically.</p><p>A station can get faster while the journey gets worse. A developer can feel more productive while the delivery system becomes more fragile. A tool can improve local output while feeding the real constraint.</p><p>The airport prints boarding passes faster. The plane still leaves late.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;8e7c7310-c3ab-48d9-940d-36e43e149d33&quot;,&quot;caption&quot;:&quot;&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Output, Outcome, and Throughput: What Product Teams are really measuring when they say they deliver value&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-05-12T05:01:35.830Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/vimeo/w_728,c_limit,d_video_placeholder.png/206617354&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/output-outcome-and-throughput-what&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:189660243,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;3965e85e-4d6e-48d8-bb9e-42775788a591&quot;,&quot;caption&quot;:&quot;Modern organizations often praise busyness as if it were the clearest sign of effectiveness. A team with no idle time appears disciplined, efficient, and productive. A room full of people constantly moving, answering, processing, and multitasking seems, at first glance, like a healthy system. Yet the core argument of&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;The Resource Utilization Trap: Why Keeping Everyone Busy Can Destroy Real Productivity&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-05-19T05:00:58.445Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!XHGN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/the-resource-utilization-trap-why&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:189661244,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>The user does not care who wrote the code</h2><p>A user does not experience your development process. They do not experience your prompts, your agents, your workflow graph, your automation scripts, your pull request template, or your internal output dashboard. They experience the product.</p><p>They experience whether the feature solves their problem. They experience whether yesterday&#8217;s behaviour still works today. They experience whether the system is available when they need it. They experience whether support can help them when something goes wrong. They experience whether they can trust the product after every release.</p><p>This is where much of the <em>AI</em> conversation goes wrong. It treats code production as if it were the final output of software development. It is not. Code is inventory until it is safely integrated, delivered, operated, learned from, and converted into value.</p><p>Generating one hundred times more code is not impressive if the user receives value at the same speed. It is worse than unimpressive if the extra code increases defects, slows review, expands cognitive load, and makes incidents harder to resolve.</p><p>A team can look faster from the inside while becoming slower from the outside. Developers see more code generated. Managers see more pull requests. Dashboards show more activity. But the user sees broken flows, delayed features, unstable releases, and slower recovery when something fails.</p><p>That is not acceleration. It is local optimisation wearing a value costume.</p><p>A simple diagram helps here, as long as it does not try to place agentic programming inside <em>XP</em>. That would make the wrong thing central. The important distinction is not how much work enters the system, but whether the system can turn that work into value without losing trust.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FDzW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c6ef15-f51e-4420-893a-845575294606_3636x588.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FDzW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c6ef15-f51e-4420-893a-845575294606_3636x588.png 424w, https://substackcdn.com/image/fetch/$s_!FDzW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c6ef15-f51e-4420-893a-845575294606_3636x588.png 848w, https://substackcdn.com/image/fetch/$s_!FDzW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c6ef15-f51e-4420-893a-845575294606_3636x588.png 1272w, https://substackcdn.com/image/fetch/$s_!FDzW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c6ef15-f51e-4420-893a-845575294606_3636x588.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FDzW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c6ef15-f51e-4420-893a-845575294606_3636x588.png" width="1456" height="235" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/73c6ef15-f51e-4420-893a-845575294606_3636x588.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:235,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:139806,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c6ef15-f51e-4420-893a-845575294606_3636x588.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_!FDzW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c6ef15-f51e-4420-893a-845575294606_3636x588.png 424w, https://substackcdn.com/image/fetch/$s_!FDzW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c6ef15-f51e-4420-893a-845575294606_3636x588.png 848w, https://substackcdn.com/image/fetch/$s_!FDzW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c6ef15-f51e-4420-893a-845575294606_3636x588.png 1272w, https://substackcdn.com/image/fetch/$s_!FDzW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c6ef15-f51e-4420-893a-845575294606_3636x588.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p><p>The real conversation is not where to place agentic workflows in a process diagram. The real conversation is whether the system can absorb generated change without damaging value, reliability, delivery, recovery, and trust.</p><h2>The story of a cheap change</h2><p>[Illustrative example] Imagine a team working on a subscription product. A new discount rule is needed for enterprise customers who renew early. The rule sounds small. The agent receives a prompt with the ticket, the relevant files, and a short explanation from the developer. In a few minutes, it modifies the pricing service, updates a couple of tests, adds a new case to the invoice preview, and writes a clean pull request description.</p><p>Everything looks efficient.</p><p>The build is green. The diff is bigger than expected, but most of it looks reasonable. The reviewer is busy. The generated explanation sounds coherent. The tests cover the happy path. The pull request is approved because the team is under pressure and the change seems low risk.</p><p>That night, invoices are generated incorrectly for a subset of customers who have an old negotiated contract. The old exception was not in the prompt. It was not in the ticket. It was not in the tests. It lived in a corner of the domain that two people knew about, one comment vaguely hinted at, and the existing code handled through an unpleasant conditional nobody liked.</p><p>Support sees the problem first. Finance escalates. Engineering starts digging. The code was generated quickly, but the intent was not preserved durably. The reviewer did not reconstruct the domain rule. The tests did not express the old contract exception. The pull request was easy to produce and expensive to trust. The team spends hours finding the affected invoices, understanding the blast radius, patching the rule, and explaining what happened.</p><p>The code was cheap. The change was expensive.</p><p>This example is not an argument against using <em>AI</em>. A human could have made the same mistake. That is the point: the fundamentals did not change.</p><p>If the behaviour matters, it needs to be understood.</p><p>If the rule matters, it needs to be expressed.</p><p>If the risk matters, it needs to be controlled.</p><p>If trust matters, the system needs feedback loops strong enough to catch wrongness before users pay for it.</p><p><em>AI</em> can help produce the change. It cannot, by itself, decide what the change must not break.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;22c4b7de-039a-4035-837c-e29502cd6af2&quot;,&quot;caption&quot;:&quot;A restaurant buys a machine that can chop vegetables three times faster than any cook. At first, everyone celebrates. Prep is flying. Bowls fill up. Ingredients appear before anyone has time to ask for them. The manager walks past the counter and sees motion everywhere. The machine looks like productivity made visible.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Faster code, slower system&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-05-14T06:00:51.102Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!ENRk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/faster-code-slower-system&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:197182453,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2><em>AI</em> does not replace Agile. It exposes fake Agile</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!U1b1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70b8f839-bdb8-4089-86da-0006b7824a9c_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!U1b1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70b8f839-bdb8-4089-86da-0006b7824a9c_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!U1b1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70b8f839-bdb8-4089-86da-0006b7824a9c_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!U1b1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70b8f839-bdb8-4089-86da-0006b7824a9c_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!U1b1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70b8f839-bdb8-4089-86da-0006b7824a9c_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!U1b1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70b8f839-bdb8-4089-86da-0006b7824a9c_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/70b8f839-bdb8-4089-86da-0006b7824a9c_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:748757,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70b8f839-bdb8-4089-86da-0006b7824a9c_1024x558.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_!U1b1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70b8f839-bdb8-4089-86da-0006b7824a9c_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!U1b1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70b8f839-bdb8-4089-86da-0006b7824a9c_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!U1b1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70b8f839-bdb8-4089-86da-0006b7824a9c_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!U1b1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70b8f839-bdb8-4089-86da-0006b7824a9c_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Agile was not supposed to mean stand-ups, Jira rituals, velocity theatre, roadmap theatre, or a sprint-shaped waterfall with better branding. The Agile Manifesto values individuals and interactions, working software, customer collaboration, and responding to change. Its principles put customer satisfaction through early and continuous delivery of valuable software first. They also emphasise frequent delivery of working software, daily collaboration between business people and developers, sustainable development, technical excellence, good design, simplicity, self-organising teams, and regular reflection.</p><p>That is not tool worship. It is a theory of software development under uncertainty.</p><p><em>AI</em> does not remove that uncertainty. It can reduce some friction in exploring possible implementations, but it does not know which customer problem matters most by itself. It does not decide what tradeoff is economically sound. It does not turn a list of disconnected tickets into strategy. It does not convert a vague roadmap into product judgment. It does not make value appear because the backlog moved faster.</p><p>In fact, <em>AI</em> can make fake Agile look better for a while.</p><p>A team can generate more of almost everything:</p><ol><li><p>Stories</p></li><li><p>Acceptance criteria</p></li><li><p>Tasks</p></li><li><p>Estimates</p></li><li><p>Summaries</p></li><li><p>Release notes</p></li><li><p>Pull requests</p></li><li><p>Diagrams</p></li><li><p>Meeting notes<br>The surface area of movement expands. The theatre becomes cheaper, faster, and more convincing.</p></li></ol><p>But if the team does not have a clear diagnosis of the problem, a coherent product strategy, a staged path of learning, and a way to validate outcomes, <em>AI</em> will only decorate confusion.</p><p>Bad Agile asks: &#8220;How many tickets did we close?&#8221; Real Agile asks: &#8220;Did valuable working software reach users sooner, with feedback that changes what we do next?&#8221;</p><p>Bad <em>AI</em> adoption asks: &#8220;How much code did we generate?&#8221; Serious <em>AI</em> adoption asks: &#8220;Did we improve the system&#8217;s ability to deliver valuable change safely?&#8221;</p><p>That distinction matters because &#8220;use <em>AI</em>&#8221; is not a strategy. It is an instruction. Strategy requires diagnosis, terrain, constraints, capabilities, tradeoffs, and coherent action. Without those things, <em>AI</em> adoption becomes another way to confuse movement with progress.</p><p>The same applies to product work. A roadmap is not a strategy, and a timeline is not a roadmap. Strategy explains the challenge and the logic of response. A roadmap communicates staged movement. A timeline schedules specific actions. Collapse those distinctions and you get visible movement, hidden confusion, and false certainty. <em>AI</em> makes that theatre cheaper to produce.</p><p>That is why <em>AI</em> does not replace Agile. It raises the cost of pretending to be Agile.</p><p>A team that truly values working software over paperwork should ask whether <em>AI</em> is helping get valuable, validated changes to users sooner. A team that truly values customer collaboration should ask whether <em>AI</em> is improving contact with reality, or just producing plausible artefacts about reality. A team that truly values responding to change should ask whether <em>AI</em> is preserving optionality, or just increasing the cost of future movement.</p><p>The Agile question is not: &#8220;Are we using the new tools?&#8221; The Agile question is: &#8220;Are we learning faster, delivering valuable software sooner, and adapting without losing technical and human sustainability?&#8221;</p><h2><em>AI</em> does not replace <em>XP</em>. It exposes weak feedback</h2><p><em>XP</em> is even more exposed to misunderstanding because many people reduce it to practices: pair programming, <em>Test-Driven Development</em> (<em>TDD</em>), refactoring, continuous integration, small releases, stories, planning cycles. But in <em>Extreme Programming Explained</em>, Kent Beck and Cynthia Andres do not present <em>XP</em> as a bag of ceremonies. They present it as a discipline for social change, technical excellence, feedback, communication, simplicity, courage, respect, and risk reduction.</p><p>That matters in the <em>AI</em> era because <em>AI</em> attacks the system at exactly the points <em>XP</em> was designed to strengthen.</p><p><em>AI</em> increases the amount of proposed change. <em>XP</em> asks for short cycles and fine-grained feedback. <em>AI</em> can produce plausible code that misses intent. <em>XP</em> asks for tests, customer involvement, and communication. <em>AI</em> can make accidental complexity easier to create. <em>XP</em> asks for simplicity, refactoring, and incremental design.</p><p><em>AI</em> can make large batches tempting because generation feels cheap. <em>XP</em> asks for small steps, frequent integration, and frequent delivery. <em>AI</em> can isolate people behind tools. <em>XP</em> asks for collaboration, shared understanding, and collective responsibility. <em>AI</em> can turn confidence into a hallucinated feeling of progress. <em>XP</em> asks for concrete feedback from tests, integration, users, and running software.</p><p>This is why <em>XP</em> was never a typing strategy. It was a feedback strategy.</p><p>The practices matter because they interact. Tests without refactoring can freeze bad design. Refactoring without tests can become unsafe editing. Continuous integration without small batches can become a slow gate at the end of a queue. Pairing without shared ownership can become isolated mentoring. Planning without real customer involvement can become backlog administration. <em>XP</em> matters because it treats software development as a system of mutually reinforcing feedback loops, not a list of isolated rituals.</p><p>Beck and Andres&#8217; principle of flow is especially relevant here. They warn that software has often delivered value in large chunks, and that when teams respond to stress by making chunks bigger, feedback gets worse and risk rises. Their direction is the opposite: deploy smaller increments of value more frequently.</p><p><em>AI</em> changes who, or what, can propose the next edit. It does not change the need to know whether the edit belongs.</p><h2><em>Test-Driven Development</em> (<em>TDD</em>) as executable intent</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!c2ks!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff92e4128-d535-47eb-b4b3-cb1182183e3c_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!c2ks!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff92e4128-d535-47eb-b4b3-cb1182183e3c_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!c2ks!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff92e4128-d535-47eb-b4b3-cb1182183e3c_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!c2ks!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff92e4128-d535-47eb-b4b3-cb1182183e3c_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!c2ks!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff92e4128-d535-47eb-b4b3-cb1182183e3c_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!c2ks!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff92e4128-d535-47eb-b4b3-cb1182183e3c_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f92e4128-d535-47eb-b4b3-cb1182183e3c_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:862572,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff92e4128-d535-47eb-b4b3-cb1182183e3c_1024x558.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_!c2ks!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff92e4128-d535-47eb-b4b3-cb1182183e3c_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!c2ks!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff92e4128-d535-47eb-b4b3-cb1182183e3c_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!c2ks!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff92e4128-d535-47eb-b4b3-cb1182183e3c_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!c2ks!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff92e4128-d535-47eb-b4b3-cb1182183e3c_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In a world where code generation is cheap, intent becomes expensive.</p><p>This is where <em>Test-Driven Development</em> (<em>TDD</em>) becomes much more than a programmer habit. It becomes a way of making intent executable before production code starts to accumulate. A test is not only a check. At its best, it is a concrete example of behaviour the system should provide. It gives the team a small, objective target. It narrows the scope of generation. It gives reviewers something better than taste to discuss.</p><p>Without executable intent, <em>AI</em> can satisfy the shape of the request while missing the reason behind it. A prompt can be ambiguous. A story can be too broad. A design note can hide assumptions. A generated implementation can look idiomatic while breaking an invariant that lived only in someone&#8217;s head. A reviewer can scan style and still miss behaviour.</p><p>The risk is not that <em>AI</em> always writes bad code. The risk is that it often writes plausible code, and plausibility is expensive to review.</p><p><em>TDD</em> changes the conversation. Before asking an agent to implement, the team asks:</p><ol><li><p>What behaviour should be true?</p></li><li><p>What example would fail today?</p></li><li><p>What existing behaviour must remain unchanged?</p></li><li><p>What edge case matters?</p></li><li><p>What would give us confidence that the change is real?</p></li><li><p>What would reveal that the generated code misunderstood the problem?<br>That is not ceremony. That is steering.</p></li></ol><p>In the <em>AI</em> era, a failing test is a better prompt than a vague instruction. It says, in executable form, what reality should look like after the change. It also creates a limit. Once the test passes, the team can ask whether the implementation is simple, whether the design improved or degraded, whether the test expresses behaviour rather than structure, and whether another example is needed.</p><p>This is where <em>Test-Driven Development</em> (<em>TDD</em>) protects against one of <em>AI</em>&#8217;s strongest temptations: excess. When the cost of writing code drops, the temptation to write &#8220;just in case&#8221; code rises. <em>TDD</em> forces the next behaviour to earn its way into the system through a concrete example, a failing check, and a small step toward working software.</p><p>A team using <em>AI</em> without executable intent may get faster code. A team using <em>AI</em> with strong examples, <em>TDD</em>, and fast feedback may get safer learning.</p><p>Those are different outcomes.</p><h2><em>Trunk-Based Development</em> as integration discipline</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tkuh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35527b75-be9c-4241-a008-eac0b80c996b_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tkuh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35527b75-be9c-4241-a008-eac0b80c996b_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!tkuh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35527b75-be9c-4241-a008-eac0b80c996b_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!tkuh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35527b75-be9c-4241-a008-eac0b80c996b_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!tkuh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35527b75-be9c-4241-a008-eac0b80c996b_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tkuh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35527b75-be9c-4241-a008-eac0b80c996b_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/35527b75-be9c-4241-a008-eac0b80c996b_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:602105,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35527b75-be9c-4241-a008-eac0b80c996b_1024x558.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_!tkuh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35527b75-be9c-4241-a008-eac0b80c996b_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!tkuh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35527b75-be9c-4241-a008-eac0b80c996b_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!tkuh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35527b75-be9c-4241-a008-eac0b80c996b_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!tkuh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35527b75-be9c-4241-a008-eac0b80c996b_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Generated change becomes dangerous when it stays away from the shared system for too long.</p><p>A coding agent can produce a large branch quickly. It can modify many files, create tests, change configuration, and rewrite behaviour before anyone else has integrated related work. The branch looks like progress because it contains a lot of movement. But until it returns to the shared line of development, it is still a private version of reality.</p><p>This is where <em>Trunk-Based Development</em> matters.</p><p><em>Trunk-Based Development</em> keeps collaboration centred on a single shared branch, usually called trunk or main. The discipline is not merely naming a branch &#8220;main.&#8221; The discipline is integrating small changes frequently, avoiding long-lived development branches, and keeping the shared codebase releasable. Hammant describes it as collaboration on a single shared branch where commits are written back to trunk, long-lived development branches are resisted, and every commit should still produce a production-ready build.</p><p>Short-lived branches can support review and automated checking, but they must remain short-lived. They should not become warehouses for generated work. Hammant explicitly describes short-lived feature branches as suitable for review and automated build checking, but not for artifact creation or publication.</p><p>In an <em>AI</em>-assisted team, this becomes more important, not less. If agents increase the rate at which changes are proposed, long-lived branches become divergence machines. Every hour a generated change stays away from trunk is an hour in which assumptions can age, neighbouring changes can conflict, tests can stop representing the real system, and reviewers can lose context.</p><p>The problem is not that branches exist. The problem is that delayed integration hides the real cost of change.</p><p><em>Trunk-Based Development</em> forces generated work to meet shared reality quickly. It keeps feedback close to the act of change. It reduces the distance between &#8220;the agent produced something&#8221; and &#8220;the team knows whether this belongs in the system.&#8221;</p><p>A pull request is not integration. A CI server is not integration. A branch named main is not <em>Trunk-Based Development</em>. Integration happens when change joins the shared line of development quickly enough that divergence does not become its own hidden system.</p><p>A team that generates code quickly but integrates slowly has not improved delivery. It has increased the speed at which divergence is created.</p><h2><em>Continuous Delivery</em> as value flow</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RaGt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9743a1-2792-4ca9-92f2-d502cba921d9_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RaGt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9743a1-2792-4ca9-92f2-d502cba921d9_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!RaGt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9743a1-2792-4ca9-92f2-d502cba921d9_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!RaGt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9743a1-2792-4ca9-92f2-d502cba921d9_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!RaGt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9743a1-2792-4ca9-92f2-d502cba921d9_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RaGt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9743a1-2792-4ca9-92f2-d502cba921d9_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7b9743a1-2792-4ca9-92f2-d502cba921d9_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:756750,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9743a1-2792-4ca9-92f2-d502cba921d9_1024x558.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_!RaGt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9743a1-2792-4ca9-92f2-d502cba921d9_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!RaGt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9743a1-2792-4ca9-92f2-d502cba921d9_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!RaGt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9743a1-2792-4ca9-92f2-d502cba921d9_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!RaGt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9743a1-2792-4ca9-92f2-d502cba921d9_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Integration is not the final outcome. Integrated code is still not value until it can be safely delivered, operated, and learned from.</p><p><em>Continuous Delivery</em> is the discipline of making the path from idea to realised business value shorter, safer, more repeatable, and more reliable. Its point is not deployment theatre. Its point is reducing the time and risk between deciding that a change matters and having that change available to users.</p><p>Humble and Farley frame cycle time as the time from deciding a change is needed to having it in production, and they describe the goal as making the path from idea to realised business value shorter and safer. They also make the economic point directly: software delivers no revenue until it is in the hands of users.</p><p>That distinction matters in the <em>AI</em> era.</p><p>A coding agent can make implementation feel fast, but if releasing still takes days, weeks, or months, the user has not received value sooner. If the path to production is manual, fragile, stressful, or poorly understood, faster code production just creates more waiting work. If every release is risky, generated changes accumulate as inventory.</p><p><em>Continuous Delivery</em> asks a harder question: can every valuable change move through build, test, deployment, release, operation, and feedback with enough speed and confidence that the team can learn from reality?</p><p>That is effectiveness.</p><p>Not code generated. Not pull requests opened. Not tickets moved. Usable software, safely delivered, in the hands of users.</p><p>Humble and Farley&#8217;s framing is useful here because they do not treat delivery as mere speed. They connect delivery to feedback: any change should trigger feedback, feedback should arrive as soon as possible, and the delivery team should receive and act on it. Their feedback process includes build, unit tests, quality checks, acceptance tests, nonfunctional tests, exploratory testing, and customer demonstration where relevant.</p><p>In a world with <em>AI</em> coding agents, <em>Continuous Delivery</em> becomes the economic test of the whole system. It reveals whether faster production becomes earlier value, or whether it becomes a larger queue in front of release.</p><p>The point is not to generate more change. The point is to make valuable change safely releasable.</p><h2>Refactoring as economic control</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Hw13!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17dcd5f7-5bdc-4b6c-9793-b4f0c10c46a8_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Hw13!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17dcd5f7-5bdc-4b6c-9793-b4f0c10c46a8_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!Hw13!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17dcd5f7-5bdc-4b6c-9793-b4f0c10c46a8_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!Hw13!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17dcd5f7-5bdc-4b6c-9793-b4f0c10c46a8_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!Hw13!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17dcd5f7-5bdc-4b6c-9793-b4f0c10c46a8_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Hw13!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17dcd5f7-5bdc-4b6c-9793-b4f0c10c46a8_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17dcd5f7-5bdc-4b6c-9793-b4f0c10c46a8_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:596904,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17dcd5f7-5bdc-4b6c-9793-b4f0c10c46a8_1024x558.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_!Hw13!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17dcd5f7-5bdc-4b6c-9793-b4f0c10c46a8_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!Hw13!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17dcd5f7-5bdc-4b6c-9793-b4f0c10c46a8_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!Hw13!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17dcd5f7-5bdc-4b6c-9793-b4f0c10c46a8_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!Hw13!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17dcd5f7-5bdc-4b6c-9793-b4f0c10c46a8_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Refactoring is often reduced to &#8220;cleaning code.&#8221; That reduction is dangerous. Martin Fowler defines refactoring as changing the internal structure of software to make it easier to understand and cheaper to modify without changing observable behaviour.</p><p>That definition gives us the economic point.</p><p>Refactoring is not aesthetic polishing. It is a way to manage the future cost of change. It protects understandability, modifiability, and design integrity. It helps keep the cost of the next valuable change from rising silently. It is how a team pays down accidental complexity before it compounds into delivery drag.</p><p><em>AI</em> makes this more important because generated code can add behaviour faster than it preserves conceptual integrity.</p><p>An agent can fit into local style while still weakening the model. It can duplicate logic rather than discovering the right abstraction. It can add conditionals where a better responsibility split is needed. It can satisfy the visible test while making the next change harder. It can produce code that is syntactically acceptable and semantically expensive.</p><p>The danger is not only bad code. The danger is adequate code in the wrong place.</p><p>This is where <em>XP</em>&#8217;s incremental design matters. Incremental design is not speculative architecture. It is the daily investment required to keep the design aligned with what the team now understands. That is the opposite of letting generated patches accumulate until the codebase becomes a landfill of local solutions.</p><p><em>AI</em>-assisted development needs refactoring as economic hygiene. Not heroic rewrites. Not platform dreams disguised as cleanliness. Not future-proofing based on imagined requirements. It needs opportunistic refactoring, guided by the next change, protected by tests, limited by economic sense, and aimed at making imminent or likely future change cheaper.</p><p>If agents make it cheaper to add code, refactoring helps decide whether that code deserves to stay in the shape it first arrived.</p><h2>Small batches as protection against generated inventory</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WOVw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe715d5e2-04d1-4706-9258-20db9f5f8c3e_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WOVw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe715d5e2-04d1-4706-9258-20db9f5f8c3e_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!WOVw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe715d5e2-04d1-4706-9258-20db9f5f8c3e_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!WOVw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe715d5e2-04d1-4706-9258-20db9f5f8c3e_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!WOVw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe715d5e2-04d1-4706-9258-20db9f5f8c3e_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WOVw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe715d5e2-04d1-4706-9258-20db9f5f8c3e_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e715d5e2-04d1-4706-9258-20db9f5f8c3e_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:916836,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe715d5e2-04d1-4706-9258-20db9f5f8c3e_1024x558.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_!WOVw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe715d5e2-04d1-4706-9258-20db9f5f8c3e_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!WOVw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe715d5e2-04d1-4706-9258-20db9f5f8c3e_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!WOVw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe715d5e2-04d1-4706-9258-20db9f5f8c3e_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!WOVw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe715d5e2-04d1-4706-9258-20db9f5f8c3e_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Cheap generation creates a psychological trap: because code is easier to produce, larger changes feel less costly.</p><p>They are not.</p><p>The cost of a batch is not only the time spent typing it. The cost of a batch includes review time, context reconstruction, test scope, integration risk, deployment risk, rollback difficulty, user impact, incident diagnosis, and the opportunity cost of delayed feedback.</p><p>Large batches hide mistakes. They increase cognitive load. They make review less precise. They delay learning. They expand blast radius. They make it harder to know which part of a change caused a problem. <em>AI</em> does not remove those costs. It can make them easier to trigger.</p><p>This is why small batches are not an old Agile preference. They are a control mechanism.</p><p>A small change is easier to understand, test, review, integrate, release, roll back, learn from, delete, and explain to a user. It is also easier to connect to a business outcome.</p><p>When generation is cheap, teams need stronger batch discipline, not weaker discipline.</p><p>The temptation will be to ask the agent for the whole flow: schema change, backend endpoint, frontend screen, tests, documentation, migration, release notes, and maybe a refactor on the way. Sometimes that may be useful for exploration. But as delivery work, large generated batches can become review traps. The reviewer must reconstruct intent across too many files. The test suite may say green while important product assumptions remain untested. The pull request looks efficient because it contains a lot of work, but the system absorbs it slowly.</p><p>Small batches keep generated change accountable.</p><p>They force the team to ask: what is the smallest behaviour that teaches us something or creates value? What is the smallest safe release? What is the smallest reversible step? What is the smallest slice that lets users, tests, integration, and production give us feedback?</p><p>That is Agile in the real sense: learning through small, valuable, working increments.</p><h2>Pairing, review, and shared judgment</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f_ZK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43c83831-31cd-4c23-8f5e-cf02d2e1286c_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f_ZK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43c83831-31cd-4c23-8f5e-cf02d2e1286c_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!f_ZK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43c83831-31cd-4c23-8f5e-cf02d2e1286c_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!f_ZK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43c83831-31cd-4c23-8f5e-cf02d2e1286c_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!f_ZK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43c83831-31cd-4c23-8f5e-cf02d2e1286c_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!f_ZK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43c83831-31cd-4c23-8f5e-cf02d2e1286c_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/43c83831-31cd-4c23-8f5e-cf02d2e1286c_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:951134,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43c83831-31cd-4c23-8f5e-cf02d2e1286c_1024x559.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_!f_ZK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43c83831-31cd-4c23-8f5e-cf02d2e1286c_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!f_ZK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43c83831-31cd-4c23-8f5e-cf02d2e1286c_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!f_ZK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43c83831-31cd-4c23-8f5e-cf02d2e1286c_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!f_ZK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43c83831-31cd-4c23-8f5e-cf02d2e1286c_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Some people hear &#8220;<em>AI</em> coding agent&#8221; and assume pair programming becomes less relevant. That conclusion depends on misunderstanding pairing as two humans sharing a keyboard because typing was scarce.</p><p>Typing was not the point. Shared judgment was the point.</p><p>Pairing helps with attention, design discussion, naming, problem framing, risk perception, knowledge transfer, and immediate feedback. It helps make decisions visible while they are still cheap to change. It reduces the loneliness of difficult work. It spreads context. It catches misunderstandings early.</p><p><em>AI</em> does not replace that. It changes the shape of the collaboration.</p><p>Sometimes the useful pairing will be human-human, where two people use <em>AI</em> as a tool while preserving shared intent. Sometimes it will be human-agent, where the human writes tests, constrains scope, asks for alternatives, and rejects plausible but wrong output. Sometimes it will be review-pairing, where two humans inspect a generated change because the risk is high, the domain is subtle, or the design tradeoff matters.</p><p>The key is not the seating arrangement. The key is whether the team protects shared understanding.</p><p>This is especially important because <em>AI</em> can produce work that feels finished before the team has understood it. A generated implementation may compress the visible act of coding, but it can also move reasoning out of the team&#8217;s heads. The code appears. The explanation sounds plausible. The tests pass. The pull request looks complete.</p><p>Before treating generated work as finished, the team should be able to answer:</p><ol><li><p>Why is this design right?</p></li><li><p>Which alternatives were rejected?</p></li><li><p>Which assumptions did the agent make?</p></li><li><p>What should we do if this change fails in production?<br>If the answer is no, the team did not save time. It borrowed time from the future at uncertain interest.</p></li></ol><p>Review has the same problem. Review is not a cleaning service for generated code. It is a scarce decision point in the system. If review becomes the place where all upstream ambiguity is dumped, senior engineers become the airport security queue. Every generated change reaches them faster, larger, and more polished on the surface, while the deeper work of intent validation becomes heavier.</p><p>Good teams will not solve this by asking reviewers to work harder. They will improve the quality of work before review: clearer intent, smaller batches, better tests, stronger local verification, simpler design, better prompts, and better rules for what agents are allowed to change without human attention.</p><p>That is not anti-<em>AI</em>. That is respect for the constraint.</p><h2>&#8220;Use <em>AI</em>&#8221; is not a strategy</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CzfY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6eaee-31b3-4ee2-bf70-4ede07a9baba_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CzfY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6eaee-31b3-4ee2-bf70-4ede07a9baba_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!CzfY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6eaee-31b3-4ee2-bf70-4ede07a9baba_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!CzfY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6eaee-31b3-4ee2-bf70-4ede07a9baba_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!CzfY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6eaee-31b3-4ee2-bf70-4ede07a9baba_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CzfY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6eaee-31b3-4ee2-bf70-4ede07a9baba_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/38c6eaee-31b3-4ee2-bf70-4ede07a9baba_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1092080,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6eaee-31b3-4ee2-bf70-4ede07a9baba_1024x558.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_!CzfY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6eaee-31b3-4ee2-bf70-4ede07a9baba_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!CzfY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6eaee-31b3-4ee2-bf70-4ede07a9baba_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!CzfY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6eaee-31b3-4ee2-bf70-4ede07a9baba_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!CzfY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6eaee-31b3-4ee2-bf70-4ede07a9baba_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There is a familiar feeling around many <em>AI</em> initiatives.</p><p>Lots of pilots. Lots of excitement. A growing story that this changes everything. But beneath the noise, the pattern often looks less like transformation and more like premature adoption:</p><ol><li><p>Use cases not clearly tied to business outcomes</p></li><li><p>Teams experimenting without a clear direction</p></li><li><p>Success metrics that are vague, missing, or too local</p></li><li><p>Limited measurable impact on value, quality, delivery, or learning<br>We have seen versions of this pattern before. Cloud promised speed and flexibility. Microservices promised scalability and independent evolution. Data platforms promised better decisions. None of those ideas were wrong. The problem was not the technology itself. The problem was adoption moving faster than understanding.</p></li></ol><p><em>AI</em> can fall into the same trap.</p><p>The question is not: &#8220;Where can we apply <em>AI</em>?&#8221;</p><p>The better question is: &#8220;Where are we currently losing time, money, quality, or trust, and can <em>AI</em> meaningfully improve that constraint?&#8221;</p><p>Without that shift, many <em>AI</em> initiatives will remain interesting experiments rather than real transformation.</p><p>A strategy has a diagnosis, a guiding policy, and coherent action. It explains what is happening, what matters, where advantage may come from, what constraints shape the terrain, and which actions fit together. &#8220;Use <em>AI</em>&#8221; does none of that.</p><p>It is a command.</p><p>It may be a useful command in some contexts. It may create exploration. It may reduce friction. It may reveal opportunities. But by itself, it does not say where the system is constrained, what outcome should improve, how risks will be managed, what tradeoffs are acceptable, or what evidence would make the team change direction.</p><p>That is why <em>AI</em> adoption often becomes another version of roadmap theatre. Leaders ask for visible movement. Teams produce artefacts. Dashboards show activity. Everyone can point to examples.</p><p>But the deeper questions remain unanswered:</p><ol><li><p>What user problem are we solving better because of <em>AI</em>?</p></li><li><p>Which part of the delivery system is constrained?</p></li><li><p>Are we reducing lead time, or increasing work in progress?</p></li><li><p>Are we improving quality, or increasing review burden?</p></li><li><p>Are we strengthening product judgment, or generating plausible stories faster?</p></li><li><p>Are we lowering the cost of future change, or increasing basal cost?</p></li><li><p>Are we improving recovery from incidents, or increasing operational fragility?<br>Without those questions, <em>AI</em> adoption becomes the corporate equivalent of printing boarding passes faster while passengers miss flights.</p></li></ol><p>This is where Agile and <em>XP</em> provide a useful discipline, not because they are fashionable, but because they point back to reality. Agile asks whether we are delivering valuable working software early and continuously. <em>XP</em> asks whether we have the feedback loops, technical excellence, collaboration, tests, integration, and design discipline to change software safely. <em>Trunk-Based Development</em> asks whether change is joining shared reality quickly. <em>Continuous Delivery</em> asks whether integrated change can become user value safely and repeatedly. Software economics asks whether the investment creates value after cost, risk, delay, and future carrying cost are considered. <em>Theory of Constraints</em> (<em>TOC</em>) asks whether we are improving the system constraint or flooding it. Product strategy asks whether our movement has a coherent logic.</p><p><em>AI</em> does not replace any of these questions.</p><p>It makes them harder to dodge.</p><h2>What teams should measure now</h2><p>A team that measures <em>AI</em> success only through usage is not measuring success. It is measuring exposure.</p><p>Accepted suggestions, prompts sent, generated lines, <em>AI</em>-authored pull requests, and tool adoption may be useful operational signals, but they do not tell us whether software delivery improved. They say the team used the tool. They do not say the tool improved the system.</p><p>A better measurement set should connect <em>AI</em> use to the outcomes software is actually accountable for.</p><p>For production and local efficiency, measure:</p><ol><li><p>Generated code volume</p></li><li><p>Accepted suggestions</p></li><li><p>Time spent prompting</p></li><li><p>Time spent reviewing generated output</p></li><li><p>Time spent correcting generated output</p></li><li><p>Repetitive work removed<br>These signals are useful, but they are not enough. They tell you whether the machine is producing more and whether local work feels cheaper. They do not tell you whether users receive value sooner.</p></li></ol><p>For delivery effectiveness, measure:</p><ol><li><p>Lead time from idea to production</p></li><li><p>Lead time from commit to production</p></li><li><p>Deployment frequency</p></li><li><p>Work in progress</p></li><li><p>Batch size</p></li><li><p>Pull request size</p></li><li><p>Review waiting time</p></li><li><p>Time in review</p></li><li><p>Reopened work</p></li><li><p>Rework rate<br>These signals help reveal whether <em>AI</em> is improving flow or increasing unfinished inventory.</p></li></ol><p>For quality and safety, measure:</p><ol><li><p>Change failure rate</p></li><li><p>Failed deployment recovery time</p></li><li><p>Deployment rework rate</p></li><li><p>Incidents per change</p></li><li><p>Escaped defects</p></li><li><p>Regression rate</p></li><li><p>Flaky test rate</p></li><li><p>Test execution time</p></li><li><p>Security findings introduced by change</p></li><li><p>Mutation testing signal where appropriate<br>These signals help reveal whether generated change is becoming trustworthy software or merely passing through weak gates.</p></li></ol><p>For trust and operability, measure:</p><ol><li><p>Mean time to detect incidents</p></li><li><p>Mean time to restore service</p></li><li><p>Rollback success</p></li><li><p>Observability coverage for changed flows</p></li><li><p>Support contacts linked to recent changes</p></li><li><p>User-facing reliability</p></li><li><p>Error budget consumption</p></li><li><p>On-call load</p></li><li><p>Cognitive load around incident diagnosis</p></li><li><p>Alert fatige</p></li><li><p>Time needed to explain a generated change after merge<br>These signals help reveal whether the system remains understandable and recoverable when change accelerates.</p></li></ol><p>For product value, measure:</p><ol><li><p>User outcome movement</p></li><li><p>Adoption of delivered capabilities</p></li><li><p>Drop-off in changed flows</p></li><li><p>Customer-reported friction</p></li><li><p>Revenue, retention, cost reduction, or risk reduction where relevant</p></li><li><p>Time from release to learning</p></li><li><p>Product bets validated or invalidated</p></li><li><p>Strategic coherence of the roadmap<br>These signals help reveal whether generated work is becoming valuable change, not merely internal motion.</p></li></ol><p>For maintainability, measure:</p><ol><li><p>Files touched per change</p></li><li><p>Change coupling</p></li><li><p>Hotspots</p></li><li><p>Complexity trends</p></li><li><p>Duplication trends</p></li><li><p>Code churn</p></li><li><p>Ownership clarity</p></li><li><p>Test readability</p></li><li><p>Design coherence around core domain concepts</p></li><li><p>Time needed for a new team member to understand a changed area<br>These signals help reveal whether the codebase remains economically changeable after the generated work is merged.</p></li></ol><p>The point is not to create a dashboard museum. The point is to stop pretending that faster code generation is enough evidence.</p><p>A team should be able to say: after adopting <em>AI</em> in this workflow, our lead time improved without harming stability, our review queues stayed healthy, our test quality improved, our incidents did not increase, our recovery remained fast, our users received value earlier, and our codebase remained easier to change.</p><p>If the team cannot say something like that, it may still be getting value from <em>AI</em>. But it does not yet know the system-level price.</p><h2>Conclusions</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jL3-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28bbe337-cc10-42a6-9cef-79acf545e972_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jL3-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28bbe337-cc10-42a6-9cef-79acf545e972_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!jL3-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28bbe337-cc10-42a6-9cef-79acf545e972_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!jL3-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28bbe337-cc10-42a6-9cef-79acf545e972_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!jL3-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28bbe337-cc10-42a6-9cef-79acf545e972_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jL3-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28bbe337-cc10-42a6-9cef-79acf545e972_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/28bbe337-cc10-42a6-9cef-79acf545e972_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:801439,&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://emmanuelvalverderamos.substack.com/i/198226577?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28bbe337-cc10-42a6-9cef-79acf545e972_1024x558.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_!jL3-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28bbe337-cc10-42a6-9cef-79acf545e972_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!jL3-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28bbe337-cc10-42a6-9cef-79acf545e972_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!jL3-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28bbe337-cc10-42a6-9cef-79acf545e972_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!jL3-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28bbe337-cc10-42a6-9cef-79acf545e972_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>AI</em> has changed software development. It has made code easier to produce. It has made examples easier to explore. It has made some tasks faster. It has made documentation, explanation, scaffolding, migration, and test generation more accessible. It has opened useful possibilities for teams willing to learn carefully.</p><p>But it has not changed what matters.</p><p>The user still does not care how much code was generated. The user cares whether the product works, helps, improves, and can be trusted. The business still does not care about generated volume by itself. It cares about value, cost, risk, time to market, recovery, optionality, and return. The system still does not care whether a human, an agent, or a workflow produced the change. It cares whether the change can be safely integrated into a living product and turned into value.</p><p>That is why <em>AI</em> does not replace <em>XP</em> or Agile. It raises the cost of pretending.</p><p>Fake Agile can produce more artefacts now. Weak <em>XP</em> can produce more code now. Poor strategy can generate more movement now. Fragile systems can receive more changes now. But none of that means users receive better value sooner.</p><p>In a world where code is expensive to write, teams can hide behind scarcity. In a world where code is cheap to generate, teams are exposed. Their real constraints become visible: unclear intent, weak tests, delayed integration, large batches, poor design, overloaded review, fragile delivery, weak operability, and shallow product judgment.</p><p>Agile and <em>XP</em> were always supposed to address those problems. Not as ceremonies. Not as nostalgia. Not as branding. As disciplines for staying connected to reality while changing software.</p><p><em>Trunk-Based Development</em> keeps generated change close to shared reality. <em>Continuous Delivery</em> keeps integrated change connected to value. <em>Test-Driven Development</em> (<em>TDD</em>) keeps implementation connected to intent. Refactoring keeps the future cost of change visible. Small batches keep risk small enough to reason about. Pairing and review keep judgment shared.</p><p>The teams that benefit most from <em>AI</em> will not be the ones that generate the most code. They will be the ones that can convert generated change into valuable, reliable, maintainable software without losing trust. They will use <em>AI</em>, but they will not let <em>AI</em> set the rhythm of the system. They will protect feedback. They will keep batches small. They will test behaviour. They will integrate frequently. They will refactor opportunistically. They will collaborate around intent. They will measure value, not theatre.</p><p><em>AI</em> has made it cheaper to propose change. It has not made it cheaper to be wrong.</p><p>The hard part was not typing before, and it is not typing now. The hard part is absorbing change without losing value, reliability, understanding, and trust.</p><h2>References</h2><ul><li><p>Beck, Kent, and Andres, Cynthia. <em>Extreme Programming Explained: Embrace Change, Second Edition</em>. Addison-Wesley Professional, 2004</p></li><li><p>Beck, Kent, Beedle, Mike, van Bennekum, Arie, Cockburn, Alistair, Cunningham, Ward, Fowler, Martin, Grenning, James, Highsmith, Jim, Hunt, Andrew, Jeffries, Ron, Kern, Jon, Marick, Brian, Martin, Robert C., Mellor, Steve, Schwaber, Ken, Sutherland, Jeff, and Thomas, Dave. <em>Manifesto for Agile Software Development</em>. </p><p><a href="https://agilemanifesto.org/">https://agilemanifesto.org/</a></p></li><li><p>Beck, Kent, Beedle, Mike, van Bennekum, Arie, Cockburn, Alistair, Cunningham, Ward, Fowler, Martin, Grenning, James, Highsmith, Jim, Hunt, Andrew, Jeffries, Ron, Kern, Jon, Marick, Brian, Martin, Robert C., Mellor, Steve, Schwaber, Ken, Sutherland, Jeff, and Thomas, Dave. <em>Principles behind the Agile Manifesto</em>. <a href="https://agilemanifesto.org/principles.html">https://agilemanifesto.org/principles.html</a></p></li><li><p>Becker, Joel, Rush, Nate, Barnes, Beth, and Rein, David. <em>Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity</em>. <a href="https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/">https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/</a></p></li><li><p>DORA. <em>State of AI-assisted Software Development 2025</em>. <a href="https://dora.dev/dora-report-2025/">https://dora.dev/dora-report-2025/</a></p></li><li><p>Faros AI. <em>AI Impact on Engineering Productivity: 2026 Report Data</em>. <a href="https://www.faros.ai/research/ai-acceleration-whiplash">https://www.faros.ai/research/ai-acceleration-whiplash</a></p></li><li><p>Faros AI. <em>The AI Engineering Report 2026: The AI Acceleration Whiplash, Ten Takeaways</em>. <a href="https://www.faros.ai/blog/ai-acceleration-whiplash-takeaways">https://www.faros.ai/blog/ai-acceleration-whiplash-takeaways</a></p></li><li><p>Fowler, Martin. <em>Continuous Integration</em>. <a href="https://martinfowler.com/articles/continuousIntegration.html">https://martinfowler.com/articles/continuousIntegration.html</a></p></li><li><p>Fowler, Martin. <em>Definition of Refactoring</em>. <a href="https://martinfowler.com/bliki/DefinitionOfRefactoring.html">https://martinfowler.com/bliki/DefinitionOfRefactoring.html</a></p></li><li><p>Google Cloud. <em>Announcing the 2024 DORA report</em>. <a href="https://cloud.google.com/blog/products/devops-sre/announcing-the-2024-dora-report">https://cloud.google.com/blog/products/devops-sre/announcing-the-2024-dora-report</a></p></li><li><p>Hammant, Paul. <em>Trunk-Based Development And Branch By Abstraction</em>. Leanpub, 2020. <a href="http://leanpub.com/trunk-based-development">http://leanpub.com/trunk-based-development</a></p></li><li><p>Humble, Jez, and Farley, David. <em>Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation</em>. Addison-Wesley Professional, 2010</p></li><li><p>Peng, Sida, Kalliamvakou, Eirini, Cihon, Peter, and Demirer, Mert. <em>The Impact of AI on Developer Productivity: Evidence from GitHub Copilot</em>. <a href="https://arxiv.org/abs/2302.06590">https://arxiv.org/abs/2302.06590</a></p></li><li><p>Valverde Ramos, Emmanuel. <em>Faster code, slower system</em>. <a href="https://emmanuelvalverderamos.substack.com/p/faster-code-slower-system">https://emmanuelvalverderamos.substack.com/p/faster-code-slower-system</a></p></li><li><p>Valverde Ramos, Emmanuel. <em>Cheap code is not cheap Software</em>. <a href="https://emmanuelvalverderamos.substack.com/p/cheap-code-is-not-cheap-software">https://emmanuelvalverderamos.substack.com/p/cheap-code-is-not-cheap-software</a></p></li><li><p>Valverde Ramos, Emmanuel. <em>Use AI is not a strategy</em>. <a href="https://emmanuelvalverderamos.substack.com/p/use-ai-is-not-a-strategy">https://emmanuelvalverderamos.substack.com/p/use-ai-is-not-a-strategy</a></p></li><li><p>Valverde Ramos, Emmanuel. <em>Product strategy is not a roadmap, and a roadmap is not a timeline</em>. <a href="https://emmanuelvalverderamos.substack.com/p/product-strategy-is-not-a-roadmap">https://emmanuelvalverderamos.substack.com/p/product-strategy-is-not-a-roadmap</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[The Resource Utilization Trap: Why Keeping Everyone Busy Can Destroy Real Productivity]]></title><description><![CDATA[Modern organizations often praise busyness as if it were the clearest sign of effectiveness.]]></description><link>https://emmanuelvalverderamos.substack.com/p/the-resource-utilization-trap-why</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/the-resource-utilization-trap-why</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Tue, 19 May 2026 05:00:58 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!XHGN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Modern organizations often praise busyness as if it were the clearest sign of effectiveness. A team with no idle time appears disciplined, efficient, and productive. A room full of people constantly moving, answering, processing, and multitasking seems, at first glance, like a healthy system. Yet the core argument of <em>The Resource Utilization Trap</em> is that this intuition is deeply misleading. The video shows, with unusual simplicity and force, that an obsession with keeping people busy can damage the very thing organizations are supposed to deliver: value flowing to the customer.</p><p>Using a ping-pong ball and a small group of people, the speaker builds a deceptively simple demonstration. Each ball represents a customer request or a feature. For the work to be complete, the ball must pass through every member of the team and then reach delivery. This physical model allows the speaker to contrast three different ways of organizing work. The first reveals that low resource utilization can coexist with fast delivery. The second shows how maximizing utilization can collapse the system entirely. The third proposes a leaner alternative: optimize flow first, and only then improve utilization, but without damaging flow.</p><p>The strength of the video lies in this central reversal: what many managers treat as waste may actually be the condition that makes delivery possible, while what many managers celebrate as efficiency may actually be the mechanism that stops delivery altogether.</p><div id="youtube2-CostXs2p6r0" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;CostXs2p6r0&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/CostXs2p6r0?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2><strong>What the demonstration is really about</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EBul!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5793e391-3491-42ea-a35d-db012f601fd0_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EBul!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5793e391-3491-42ea-a35d-db012f601fd0_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!EBul!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5793e391-3491-42ea-a35d-db012f601fd0_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!EBul!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5793e391-3491-42ea-a35d-db012f601fd0_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!EBul!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5793e391-3491-42ea-a35d-db012f601fd0_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EBul!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5793e391-3491-42ea-a35d-db012f601fd0_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5793e391-3491-42ea-a35d-db012f601fd0_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1584968,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/189661244?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5793e391-3491-42ea-a35d-db012f601fd0_1408x752.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_!EBul!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5793e391-3491-42ea-a35d-db012f601fd0_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!EBul!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5793e391-3491-42ea-a35d-db012f601fd0_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!EBul!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5793e391-3491-42ea-a35d-db012f601fd0_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!EBul!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5793e391-3491-42ea-a35d-db012f601fd0_1408x752.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>At a superficial level, the video compares three team setups. At a deeper level, it compares two philosophies of management.</p><p>The first philosophy assumes that the main problem in an organization is unused capacity. If some people are not constantly occupied, then the system appears inefficient. The obvious solution, from this perspective, is to make sure everyone is always doing something. Activity becomes the proxy for value. Motion becomes the proxy for progress. Full calendars, full queues, and full workloads become signs of managerial success.</p><p>The second philosophy starts elsewhere. It asks not whether every individual is always busy, but whether the work itself moves smoothly from request to delivery. In this view, the important unit is not the isolated worker but the whole system. The relevant question is not &#8220;Are people occupied?&#8221; but &#8220;Is the customer receiving the result quickly and reliably?&#8221; Instead of measuring individual busyness, this approach measures the movement of value across the entire chain.</p><p>The video clearly sides with the second philosophy. Its thesis is not that utilization is irrelevant, nor that idle time is always desirable, but that utilization must be subordinated to flow. Once flow is healthy, utilization can be improved carefully. When utilization becomes the primary target, the system can become busy, congested, and ultimately sterile.</p><h2><strong>Scenario one: low utilization, fast flow</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tvqP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11f97cac-1ce5-4b87-98ce-dabb41e369b3_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tvqP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11f97cac-1ce5-4b87-98ce-dabb41e369b3_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!tvqP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11f97cac-1ce5-4b87-98ce-dabb41e369b3_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!tvqP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11f97cac-1ce5-4b87-98ce-dabb41e369b3_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!tvqP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11f97cac-1ce5-4b87-98ce-dabb41e369b3_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tvqP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11f97cac-1ce5-4b87-98ce-dabb41e369b3_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/11f97cac-1ce5-4b87-98ce-dabb41e369b3_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1981461,&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://emmanuelvalverderamos.substack.com/i/189661244?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11f97cac-1ce5-4b87-98ce-dabb41e369b3_1408x752.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_!tvqP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11f97cac-1ce5-4b87-98ce-dabb41e369b3_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!tvqP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11f97cac-1ce5-4b87-98ce-dabb41e369b3_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!tvqP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11f97cac-1ce5-4b87-98ce-dabb41e369b3_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!tvqP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11f97cac-1ce5-4b87-98ce-dabb41e369b3_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The first scenario is deliberately simple. One ping-pong ball enters the system at a time. The team passes it from hand to hand until it reaches delivery. Because only one ball is moving through the process, only one pair of hands is active at any moment. The rest of the team appears idle.</p><p>From the perspective of a manager obsessed with utilization, this looks inefficient. If only one hand out of eight is working at a time, then much of the available labor seems wasted. Yet the important fact is that the ball gets delivered quickly. According to the speaker, the flow time is roughly five seconds. The customer receives the result. The system works.</p><p>This first scenario contains the first major lesson of the video: <strong>low local utilization can coexist with strong global performance</strong>. In other words, <strong>parts of the system may appear underused while the system as a whole performs well</strong>. That distinction matters because organizations often confuse local efficiency with overall effectiveness. They see waiting and conclude there must be a problem, when in fact the waiting may simply reflect the rhythm required for smooth coordination.</p><p>What this scenario reveals is that <strong>not all apparent idleness is waste</strong>. Sometimes it is slack, and slack is what allows work to move. The people who are not touching the ball in a given second are not necessarily a sign of dysfunction; they are part of a process that is successfully delivering. The system is not optimized to keep every hand busy every instant. It is optimized to complete the job.</p><p>This is a subtle but important shift. The value of a team does not lie in the number of simultaneous motions it can exhibit. Its value lies in how effectively it can transform requests into completed outcomes.</p><h2><strong>The manager&#8217;s mistake: confusing visible activity with value</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5twq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a7ac190-6471-4a6e-adfe-cc80f8f489f0_2816x1504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5twq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a7ac190-6471-4a6e-adfe-cc80f8f489f0_2816x1504.png 424w, https://substackcdn.com/image/fetch/$s_!5twq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a7ac190-6471-4a6e-adfe-cc80f8f489f0_2816x1504.png 848w, https://substackcdn.com/image/fetch/$s_!5twq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a7ac190-6471-4a6e-adfe-cc80f8f489f0_2816x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!5twq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a7ac190-6471-4a6e-adfe-cc80f8f489f0_2816x1504.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5twq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a7ac190-6471-4a6e-adfe-cc80f8f489f0_2816x1504.png" width="1456" height="778" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5a7ac190-6471-4a6e-adfe-cc80f8f489f0_2816x1504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:778,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6941567,&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://emmanuelvalverderamos.substack.com/i/189661244?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a7ac190-6471-4a6e-adfe-cc80f8f489f0_2816x1504.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_!5twq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a7ac190-6471-4a6e-adfe-cc80f8f489f0_2816x1504.png 424w, https://substackcdn.com/image/fetch/$s_!5twq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a7ac190-6471-4a6e-adfe-cc80f8f489f0_2816x1504.png 848w, https://substackcdn.com/image/fetch/$s_!5twq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a7ac190-6471-4a6e-adfe-cc80f8f489f0_2816x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!5twq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a7ac190-6471-4a6e-adfe-cc80f8f489f0_2816x1504.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The turning point of the video comes when the manager walks in. He observes several people idle and only one person working. That image triggers a familiar managerial response: this is waste, these resources are being underused, and the system must be fixed.</p><p>The mistake is immediate and structural. The manager evaluates the team through a narrow visual snapshot rather than through the system&#8217;s actual output. He sees inactivity and assumes failure, even though delivery is fast. He responds to appearances rather than to results. This is the trap.</p><p>The video is especially sharp here because it captures a common organizational reflex. Many interventions are triggered not by poor customer outcomes, but by discomfort with visible slack. A quiet employee, an empty queue, or a pause between tasks can provoke anxiety in managers who equate occupancy with value. The result is often a policy designed to eliminate idle time without asking whether that idle time was protecting the system.</p><p>In this sense, the utilization trap is not just an operational problem. It is also a perceptual and cultural problem. It depends on a particular way of seeing work. When managers trust what looks busy more than what delivers effectively, they risk optimizing the wrong thing.</p><h2><strong>Scenario two: maximum utilization, zero delivery</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!unTa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69d22970-a9b0-46fb-a7b0-ba6e7505263f_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!unTa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69d22970-a9b0-46fb-a7b0-ba6e7505263f_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!unTa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69d22970-a9b0-46fb-a7b0-ba6e7505263f_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!unTa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69d22970-a9b0-46fb-a7b0-ba6e7505263f_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!unTa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69d22970-a9b0-46fb-a7b0-ba6e7505263f_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!unTa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69d22970-a9b0-46fb-a7b0-ba6e7505263f_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/69d22970-a9b0-46fb-a7b0-ba6e7505263f_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2307292,&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://emmanuelvalverderamos.substack.com/i/189661244?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69d22970-a9b0-46fb-a7b0-ba6e7505263f_1408x752.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_!unTa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69d22970-a9b0-46fb-a7b0-ba6e7505263f_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!unTa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69d22970-a9b0-46fb-a7b0-ba6e7505263f_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!unTa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69d22970-a9b0-46fb-a7b0-ba6e7505263f_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!unTa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69d22970-a9b0-46fb-a7b0-ba6e7505263f_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In the second scenario, the manager hires a supervisor whose job is to keep people busy. Work is now pushed continuously into the team. Whenever a hand is available, another ball is placed there. The visual effect is dramatic: everyone is occupied, everyone is handling something, everyone appears fully utilized.</p><p>From one angle, this looks like an improvement. The earlier &#8220;waste&#8221; seems to have disappeared. No one is standing idle. The salaries appear fully justified. The supervisor is successful according to the metric he was hired to improve.</p><p>And yet the system has collapsed.</p><p>The balls pile up. Work becomes entangled. Nothing reaches delivery. The customer receives nothing. Flow time becomes, in the speaker&#8217;s words, effectively infinite. Throughput falls to zero. What looked like a victory in utilization becomes a catastrophe in outcomes.</p><p>This is the core paradox of the video: a system can be full of activity and empty of results.</p><p>Why does this happen? Because once work is pushed into the system faster than it can move through it, the process stops behaving like a clean sequence and starts behaving like congestion. Every new task competes for attention, space, coordination, and handling. Instead of helping the work move, the extra load traps it. The system loses the very property that made it useful: the ability to convert requests into completed deliveries.</p><p>The brilliance of the demonstration lies in how concrete this becomes. The audience does not need abstract theory to understand the problem. They can see the balls accumulating and failing to reach the customer. The point becomes undeniable: keeping every person busy does not mean the organization is productive. It may mean the organization is gridlocked.</p><h2><strong>Busy people are not the same as a productive system or even an efficient system</strong></h2><p>One of the strongest lines in the video comes near the end: if you focus on keeping people busy, what you get is a bunch of busy people.</p><p>This sentence captures the entire critique. Busyness is not the same as progress. Occupation is not the same as throughput. A manager can generate intense activity without generating delivery. In fact, the second scenario suggests that maximizing visible activity may actively undermine delivery.</p><p>This distinction matters because organizations often reward the signals of busyness. They reward overloaded calendars, rapid task switching, long queues, constant handling, and immediate response to whatever appears next. These behaviors can look like commitment and effort. But the video asks a harder question: what happens to the customer?</p><p>That question exposes the limits of utilization as a primary metric. If a team is overwhelmed with partially handled work, the customer does not benefit from the fact that everyone is exhausted. The customer benefits only when something is actually completed and delivered. A system that converts effort into delay is not efficient merely because it feels intense from the inside.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Z1xD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff651d424-d58a-4d17-8e2d-6db3c330419e_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Z1xD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff651d424-d58a-4d17-8e2d-6db3c330419e_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!Z1xD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff651d424-d58a-4d17-8e2d-6db3c330419e_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!Z1xD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff651d424-d58a-4d17-8e2d-6db3c330419e_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!Z1xD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff651d424-d58a-4d17-8e2d-6db3c330419e_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Z1xD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff651d424-d58a-4d17-8e2d-6db3c330419e_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f651d424-d58a-4d17-8e2d-6db3c330419e_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1569226,&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://emmanuelvalverderamos.substack.com/i/189661244?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff651d424-d58a-4d17-8e2d-6db3c330419e_1408x752.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_!Z1xD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff651d424-d58a-4d17-8e2d-6db3c330419e_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!Z1xD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff651d424-d58a-4d17-8e2d-6db3c330419e_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!Z1xD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff651d424-d58a-4d17-8e2d-6db3c330419e_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!Z1xD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff651d424-d58a-4d17-8e2d-6db3c330419e_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The speaker&#8217;s contrast between the happy customer and the angry customer is therefore essential. The first scenario, despite low utilization, creates customer satisfaction because work arrives. The second, despite total utilization, creates frustration because nothing arrives. The moral is clear: the system must be judged from the standpoint of delivered value, not internal busyness.</p><h2><strong>Push versus pull</strong></h2><p>The video names the second scenario a form of <strong>push</strong>. Work is pushed into the system by an external controller. The supervisor decides when more work should enter, based on the desire to keep resources occupied. The team does not regulate intake according to its real capacity. The result is overload.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!I1wX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5749c21d-32cd-450b-a67e-bb85dddc055f_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!I1wX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5749c21d-32cd-450b-a67e-bb85dddc055f_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!I1wX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5749c21d-32cd-450b-a67e-bb85dddc055f_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!I1wX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5749c21d-32cd-450b-a67e-bb85dddc055f_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!I1wX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5749c21d-32cd-450b-a67e-bb85dddc055f_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!I1wX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5749c21d-32cd-450b-a67e-bb85dddc055f_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5749c21d-32cd-450b-a67e-bb85dddc055f_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2178015,&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://emmanuelvalverderamos.substack.com/i/189661244?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5749c21d-32cd-450b-a67e-bb85dddc055f_1408x752.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_!I1wX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5749c21d-32cd-450b-a67e-bb85dddc055f_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!I1wX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5749c21d-32cd-450b-a67e-bb85dddc055f_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!I1wX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5749c21d-32cd-450b-a67e-bb85dddc055f_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!I1wX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5749c21d-32cd-450b-a67e-bb85dddc055f_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The proposed alternative is <strong>pull</strong>. Instead of having work forced upon them, the team takes in new work when it has the capacity to handle it. This is not a call for laziness or drift. It is a disciplined way of matching demand to the system&#8217;s actual ability to absorb and process work.</p><p>That distinction is central. Push begins from the supply of effort: &#8220;We have available hands, so let us feed them work.&#8221; Pull begins from the condition of the flow: &#8220;We will take more work when the system can move it.&#8221; Push measures success by how much enters. Pull measures success by what can move through and out.</p><p>In the video, pull changes the logic of control. The team, not the supervisor, determines the pace of intake. That change does not reduce performance. On the contrary, it improves it. By respecting capacity, the system avoids congestion. By avoiding congestion, it preserves fast delivery. And by preserving fast delivery, it creates the conditions for greater output over time.</p><p>Pull, then, is not merely a technique. It is a philosophy of restraint. It accepts that the health of a system depends not only on what it can do, but also on what it refuses to overload.</p><h2><strong>Scenario three: better utilization without sacrificing flow</strong></h2><p>The third scenario is the most important because it avoids a false conclusion. After seeing the disaster of 100% utilization, one might think the lesson is to ignore utilization entirely. The video explicitly rejects that conclusion.</p><p>Instead, the speaker argues for sequence: first optimize flow, then improve utilization, but only in ways that do not damage flow.</p><p>Under the pull approach, the team keeps the same fast delivery time while increasing the amount delivered in a given period. According to the speaker&#8217;s rough numbers, the first scenario produces around 12 balls per minute. The second produces zero. The third raises throughput to around 24 balls per minute while also increasing resource utilization from perhaps 10&#8211;15% to around 30%.</p><p>These numbers matter because they show that the critique of utilization is not anti-efficiency. The point is not that organizations should ignore capacity. The point is that capacity must be managed systemically. When managed badly, attempts to increase utilization destroy delivery. When managed well, utilization can rise naturally as a consequence of better flow.</p><p>This is a far more sophisticated message than a simple &#8220;flow good, utilization bad&#8221; slogan. The speaker&#8217;s actual argument is disciplined and balanced:</p><ul><li><p>first, establish a healthy flow;</p></li><li><p>second, preserve that flow;</p></li><li><p>third, improve utilization only within those constraints.</p></li></ul><p>That sequence is what makes the approach lean. It treats flow as primary and utilization as secondary, not because utilization lacks importance, but because it becomes meaningful only inside a functioning system.</p><h2><strong>The traffic jam analogy</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XHGN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XHGN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!XHGN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!XHGN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!XHGN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XHGN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2059556,&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://emmanuelvalverderamos.substack.com/i/189661244?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.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_!XHGN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!XHGN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!XHGN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!XHGN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F167ffba5-79c8-4983-a0fa-21d1f61cfbe4_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The closing analogy in the video is memorable because it translates the demonstration into everyday experience: 100% resource utilization equals 0% flow, just like a road network at full utilization becomes a traffic jam.</p><p>This analogy sharpens the main point by showing that the principle is not limited to office work. Traffic systems behave similarly. A road can seem to be used &#8220;efficiently&#8221; when every lane is full, but once capacity is saturated, movement slows, then stalls. At that point the system is not productive precisely because it is overused.</p><p>The analogy is effective because it reveals the difference between static and dynamic thinking. Static thinking sees a full road and says, &#8220;Good, no capacity is being wasted.&#8221; Dynamic thinking asks, &#8220;Are vehicles moving?&#8221; The same applies to organizations. A static manager sees full schedules and says, &#8220;Good, everyone is busy.&#8221; A dynamic manager asks, &#8220;Is work reaching the customer?&#8221;</p><p>This is perhaps the deepest conceptual contribution of the video. It shifts the frame from occupancy to movement. Systems exist to move something: cars, information, requests, features, decisions, products. When movement is sacrificed in order to maximize occupancy, the system defeats its own purpose.</p><h2><strong>What the video suggests about management</strong></h2><p>Although the demonstration is playful, its implications for management are serious.</p><p>First, it suggests that managers often create problems by intervening too early and measuring the wrong thing. In the first scenario, the team is already delivering effectively. The manager disrupts that effectiveness because he cannot tolerate visible idle time. His intervention is driven by a metric detached from the customer outcome.</p><p>Second, it suggests that managerial control can become counterproductive when it ignores system capacity. The supervisor in the second scenario is not incompetent in the narrow sense. He does exactly what he was asked to do: keep everyone busy. The failure comes from the objective itself. When the wrong target governs behavior, diligent management can make the system worse, not better.</p><p>Third, it suggests that good management requires trust in flow rather than obsession with appearances. A team that pulls work based on capacity may look less dramatic than a team drowning in tasks, but it may deliver far more value. This requires a cultural shift. Managers must learn to tolerate some visible slack if that slack protects movement and completion.</p><p>Finally, the video implies that organizations should evaluate success through customer delivery, lead time, and throughput, rather than through the local utilization of each person. The system exists to serve an outcome beyond itself. When internal metrics overshadow that purpose, dysfunction follows.</p><h2><strong>Why this lesson is difficult to accept</strong></h2><p>The idea that lower utilization can produce better results runs against many organizational instincts. It is difficult to accept for several reasons.</p><p>One reason is psychological. Idleness is easy to notice and easy to judge. Flow is harder to see. A manager walking past a room can instantly spot someone not actively handling something. It is much harder, in that same moment, to understand whether the overall process is healthy.</p><p>Another reason is symbolic. Busyness signals seriousness. People who are visibly overloaded may be seen as committed, important, or indispensable. By contrast, a smoother and calmer system can be misread as underperformance, even when it is delivering more.</p><p>A third reason is structural. Many organizations divide work into functions and then evaluate each function separately. In such environments, each unit is pressured to maximize its own utilization, even if the overall chain suffers. The video challenges that logic by showing that local optimization can destroy system performance.</p><p>The lesson is therefore not just technical; it is countercultural. It asks organizations to value completion over activity, movement over saturation, and system outcomes over local appearances.</p><h2><strong>A more rigorous reading of the three metrics</strong></h2><p>The video organizes the demonstration around three measures: <strong>resource utilization</strong>, <strong>flow time</strong>, and <strong>throughput</strong>.</p><p>These three are not equivalent, and the power of the video comes from showing how they interact.</p><h3><strong>1. Resource utilization</strong></h3><p>This refers to how much of the available capacity is actively in use. In the first scenario it is low. In the second it is effectively total. In the third it increases moderately without breaking the system.</p><p>The video&#8217;s argument is not that utilization is useless, but that it is dangerous when isolated from the other two measures.</p><h3><strong>2. Flow time</strong></h3><p>This is the time it takes for a request to move through the system and reach delivery. In the first scenario it is fast. In the second it becomes effectively infinite. In the third it remains fast.</p><p>Flow time is what connects the internal process to customer experience. A system with terrible flow time may feel active internally, but it is failing in practical terms.</p><h3><strong>3. Throughput</strong></h3><p>This is how much gets delivered over a period of time. In the first scenario it is decent. In the second it drops to zero. In the third it doubles.</p><p>Throughput matters because it reveals the cumulative consequence of preserving flow. A system that protects movement not only serves individual requests faster; it often delivers more in total.</p><p>Taken together, these three measures support the central conclusion: utilization must not be pursued in a way that damages flow, because damaged flow eventually destroys throughput as well.</p><h2><strong>The article&#8217;s central argument</strong></h2><p>The most rigorous way to state the argument of <em>The Resource Utilization Trap</em> is this:</p><p><strong>A team should not be optimized primarily for constant activity at the individual level, because maximizing local utilization can overload the system, increase congestion, delay delivery, and reduce overall output.</strong> <strong>The healthier approach is to optimize the flow of work first</strong>, then improve utilization only to the extent that flow remains intact. A pull-based way of organizing work supports this balance better than a push-based one.</p><p>Everything in the video supports this thesis. The first scenario establishes that low utilization can still produce strong flow. The second proves that high utilization can destroy flow altogether. The third shows that once flow is protected, utilization and throughput can both improve.</p><p>That argumentative sequence is what makes the demonstration so persuasive. It does not merely assert a principle; it stages the evidence in a visible progression.</p><h2><strong>Conclusion: from busyness to delivery</strong></h2><p>The lasting value of <em>The Resource Utilization Trap</em> lies in the clarity of its warning. Organizations often become trapped because they confuse means with ends. They begin by trying to make better use of people and end by making it harder for work to reach the customer. They celebrate motion while starving delivery. They optimize occupation and undermine output.</p><p>The video offers a cleaner discipline: focus on flow first. Ask how quickly and reliably work reaches the customer. Protect that movement. Then, and only then, look for ways to improve utilization without disturbing the system&#8217;s ability to deliver.</p><p>This is not a romantic defense of inefficiency. It is a more demanding idea of efficiency,one measured not by how full the system looks, but by how well it works.</p><p>The final formula, &#8220;100% resource utilization equals 0% flow,&#8221; is deliberately provocative, but it captures the essential warning. A system can become so full that it stops moving. When that happens, the appearance of efficiency masks the reality of failure.</p><p>The deeper lesson is simple and difficult at once: the goal of work is not to keep everyone busy. The goal of work is to get something meaningful done and delivered.</p>]]></content:encoded></item><item><title><![CDATA[Faster code, slower system]]></title><description><![CDATA[AI did not break software delivery. It exposed the constraint we were already ignoring.]]></description><link>https://emmanuelvalverderamos.substack.com/p/faster-code-slower-system</link><guid isPermaLink="false">https://emmanuelvalverderamos.substack.com/p/faster-code-slower-system</guid><dc:creator><![CDATA[Emmanuel Valverde Ramos]]></dc:creator><pubDate>Thu, 14 May 2026 06:00:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ENRk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!afZK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90c69fa9-9df3-49c6-9e02-54a2dd202df3_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!afZK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90c69fa9-9df3-49c6-9e02-54a2dd202df3_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!afZK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90c69fa9-9df3-49c6-9e02-54a2dd202df3_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!afZK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90c69fa9-9df3-49c6-9e02-54a2dd202df3_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!afZK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90c69fa9-9df3-49c6-9e02-54a2dd202df3_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!afZK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90c69fa9-9df3-49c6-9e02-54a2dd202df3_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/90c69fa9-9df3-49c6-9e02-54a2dd202df3_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1269521,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90c69fa9-9df3-49c6-9e02-54a2dd202df3_1024x559.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_!afZK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90c69fa9-9df3-49c6-9e02-54a2dd202df3_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!afZK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90c69fa9-9df3-49c6-9e02-54a2dd202df3_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!afZK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90c69fa9-9df3-49c6-9e02-54a2dd202df3_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!afZK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90c69fa9-9df3-49c6-9e02-54a2dd202df3_1024x559.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A restaurant buys a machine that can chop vegetables three times faster than any cook. At first, everyone celebrates. Prep is flying. Bowls fill up. Ingredients appear before anyone has time to ask for them. The manager walks past the counter and sees motion everywhere. The machine looks like productivity made visible.</p><p>Then dinner service starts.</p><p>The pass fills with unfinished plates. The head chef keeps stopping to check whether the ingredients match the orders. Waiters wait longer. Customers complain. More food leaves the kitchen, but more comes back. The machine did exactly what it promised. It accelerated one station. The kitchen did not become faster. It became louder, busier, and more fragile.</p><p>The machine was faster. Dinner was not.</p><p>That is the mistake many software organizations are making with <em>Artificial Intelligence</em> (AI). They are treating code generation as if it were the kitchen. It is not. It is one station inside a larger system.</p><p>The Faros <em>AI Engineering Report 2026</em> gives us one of the clearest telemetry-based pictures of this problem so far. Across approximately two years of data from 22,000 developers and 4,000 teams, Faros reports real acceleration: task completion is up, epics completed per developer are up, and code-related tasks have increased sharply. But the same report also shows the bill arriving downstream: bugs, incidents, review time, code churn, cognitive load, and rework are rising too. Faros calls this the &#8220;Acceleration Whiplash&#8221;. The phrase is good, but the underlying mechanism is older than AI. It is suboptimization.</p><p>The argument is not that AI is useless. That would be too easy, and it would be wrong. The argument is more uncomfortable.</p><p>AI is powerful precisely because it can optimize the wrong part of the system at industrial speed.</p><h2>TL;DR</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sSvX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4af5103a-b95e-43ce-afd2-436b67b4cd33_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sSvX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4af5103a-b95e-43ce-afd2-436b67b4cd33_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!sSvX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4af5103a-b95e-43ce-afd2-436b67b4cd33_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!sSvX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4af5103a-b95e-43ce-afd2-436b67b4cd33_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!sSvX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4af5103a-b95e-43ce-afd2-436b67b4cd33_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sSvX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4af5103a-b95e-43ce-afd2-436b67b4cd33_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4af5103a-b95e-43ce-afd2-436b67b4cd33_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1678795,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4af5103a-b95e-43ce-afd2-436b67b4cd33_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sSvX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4af5103a-b95e-43ce-afd2-436b67b4cd33_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!sSvX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4af5103a-b95e-43ce-afd2-436b67b4cd33_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!sSvX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4af5103a-b95e-43ce-afd2-436b67b4cd33_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!sSvX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4af5103a-b95e-43ce-afd2-436b67b4cd33_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p><strong>AI can accelerate local coding output, but local speed is not system productivity.</strong><br>A controlled GitHub Copilot experiment found that developers with access to Copilot completed a JavaScript HTTP server task 55.8% faster than the control group. Faros also reports acceleration under high AI adoption: task throughput per developer is up 33.7%, epics completed per developer are up 66.2%, and tasks completed per team with an associated PR are up 210%. That matters, but it only tells us that one station in the system became faster. It does not prove that the whole delivery system became healthier, safer, or more valuable.</p></li><li><p><strong>Software delivery is not code generation.</strong><br>Code generation is one part of a wider system that includes product judgment, specification, design, review, testing, security, deployment, operation, feedback, and future change. A pull request is output. A deployed change is closer to throughput. Real outcome appears only when users receive valuable, safe, maintainable change.</p></li><li><p><strong>The Faros data suggests that AI increased upstream output while downstream pressure grew.</strong><br>The report shows larger PRs, more files touched, longer review times, more waiting, more bugs, more incidents, more reopened work, and more code churn. That pattern is important because it does not describe simple productivity. It describes acceleration followed by absorption problems.</p></li><li><p><strong>This is not a contradiction. It is suboptimization.</strong><br>A subsystem can improve while the whole system gets worse. If code generation is not the constraint, making it faster may simply push more work into review, QA, deployment, production, or incident response. The pressure does not disappear. It accumulates around the real bottleneck.</p></li><li><p><strong>Strong foundations are not immunity.</strong><br>Faros&#8217; tenth-takeaways article says that even organizations with mature DevOps practices, high DORA metrics, and disciplined delivery processes experienced the same downstream deterioration as others. That does not mean engineering foundations do not matter. It means maturity is capacity, not infinite absorption.</p></li><li><p><strong>In </strong><em><strong>Theory of Constraints</strong></em><strong> (TOC) terms, much AI-generated code may be inventory, not throughput.</strong><br>If generated code waits for review, waits for QA, waits for deployment, comes back as rework, or creates incidents, it has not yet become value. It is work inside the system, consuming capacity and attention. More inventory is not the same as more throughput.</p></li><li><p><strong>The scarce resource is no longer code. It is judgment.</strong><br>When code becomes cheap to generate, the expensive part becomes deciding whether the change is correct, valuable, safe, reversible, coherent with the architecture, and worth carrying into the future. Review is not a cleaning service for generated code. It is a scarce decision point.</p></li><li><p><strong>Software starts as an economic liability, not an asset.</strong><br>Software becomes an economic asset only when the value it creates exceeds the cost, risk, and attention it permanently consumes. AI may reduce the cost of creating software liabilities faster than the organization improves its ability to turn them into assets.</p></li><li><p><strong>Context helps, but it does not make LLM output safe by itself.</strong><br>Better context can reduce preventable mistakes, but it does not remove LLM error, context-window limits, retrieval failures, agent variance, or the need for human judgment. The goal is not blind trust. The goal is lower preventable rework.</p></li><li><p><strong>Context management has a cost too.</strong><br>Teams often have to maintain prompts, rules, memory files, context documents, agent instructions, workflows, examples, tests, and guardrails because the tools do not reliably manage all of this by themselves. That effort is not free. It is operating expense.</p></li><li><p><strong>The leadership question changes.</strong><br>The question is not &#8220;How do we get more AI-generated code?&#8221; The question is: &#8220;Where is our system constrained, and is AI protecting, elevating, or flooding that constraint?&#8221;</p></li><li><p><strong>The conclusion is simple.</strong><br>Do not let AI set the rhythm of the system. The constraint sets the rhythm. AI is not the drum. The constraint is.</p></li></ul><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://emmanuelvalverderamos.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Crafting software is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</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><h2>Index</h2><ol><li><p>The promise was local speed</p></li><li><p>Code generation is not software delivery</p></li><li><p>The Faros data shows the bill arriving downstream</p></li><li><p>This is not a contradiction. It is suboptimization</p></li><li><p>Code generation was not the constraint</p></li><li><p>AI fed the bottleneck</p></li><li><p>The system pushed back</p></li><li><p>The scarce resource is judgment</p></li><li><p>Cheap code is not a cheap asset</p></li><li><p>What leaders should measure now</p></li><li><p>How to use AI without flooding the system</p></li><li><p>Conclusions: AI is not the drum</p></li><li><p>References</p></li></ol><h2>The promise was local speed</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AOQN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3115a28d-f7ef-49db-8781-159f444bf7ec_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AOQN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3115a28d-f7ef-49db-8781-159f444bf7ec_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!AOQN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3115a28d-f7ef-49db-8781-159f444bf7ec_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!AOQN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3115a28d-f7ef-49db-8781-159f444bf7ec_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!AOQN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3115a28d-f7ef-49db-8781-159f444bf7ec_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AOQN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3115a28d-f7ef-49db-8781-159f444bf7ec_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3115a28d-f7ef-49db-8781-159f444bf7ec_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:558320,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3115a28d-f7ef-49db-8781-159f444bf7ec_1024x559.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_!AOQN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3115a28d-f7ef-49db-8781-159f444bf7ec_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!AOQN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3115a28d-f7ef-49db-8781-159f444bf7ec_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!AOQN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3115a28d-f7ef-49db-8781-159f444bf7ec_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!AOQN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3115a28d-f7ef-49db-8781-159f444bf7ec_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There is a version of the AI productivity story that is true. We should begin there.</p><p>In a controlled experiment on GitHub Copilot, developers with access to Copilot completed a JavaScript HTTP server task 55.8% faster than the control group. That is not a minor improvement. In a bounded context, with a clear task and a narrow feedback surface, AI can reduce the time between intention and code.</p><p>Faros sees the same kind of acceleration at the level of engineering activity. In its 2026 report, task throughput per developer is up 33.7%, epics completed per developer are up 66.2%, PR merge rate per developer is up 16.2%, and tasks completed per team with an associated PR are up 210%. AI is clearly increasing local output. It is making the chopping station faster.</p><p>But the word &#8220;local&#8221; matters.</p><p>Local speed is not system speed.</p><p>A local gain is not a system gain. A developer can finish a task faster while the team delivers value more slowly. A team can merge more pull requests while production becomes less stable. An organization can increase AI adoption while increasing the cost of review, testing, incident response, coordination, and future change.</p><p>This is where the productivity debate often becomes confused. One side looks at the developer and sees speed. The other side looks at production and sees pain. Both can be looking at real evidence. They are just looking at different parts of the system.</p><p>METR&#8217;s 2025 randomized controlled trial makes that gap hard to ignore. In that study, 16 experienced open-source developers worked on 246 real tasks in repositories they already knew well. Before starting, developers expected AI to reduce completion time by 24%. Afterward, they still believed AI had reduced completion time by 20%. The measured result went the other way: with AI allowed, developers took 19% longer.</p><p>That result does not prove that AI always slows developers down. It does prove something more useful for leaders: perceived acceleration and measured system performance can diverge. The work can feel easier while the system becomes slower to deliver. That is not a paradox. That is a systems problem.</p><p>Productivity in software is not the amount of code produced per unit of time. It is the system&#8217;s ability to turn scarce attention, knowledge, and investment into valuable, safe, maintainable change.</p><h2>Code generation is not software delivery</h2><p>A restaurant is not a chopping machine. It is a coordinated system of ordering, preparation, cooking, plating, quality control, service, feedback, cleaning, and learning. A software organization is similar. Coding matters, but it is only one station inside a wider flow.</p><p>Software delivery includes product judgment, specification, design, coding, review, testing, security, deployment, observability, incident response, customer feedback, and future change. If one station accelerates faster than the others can absorb, the result is not necessarily throughput. It may be congestion.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dfip!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0c1e1d-f181-4c7e-acca-aae3816bca21_3030x210.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dfip!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0c1e1d-f181-4c7e-acca-aae3816bca21_3030x210.png 424w, https://substackcdn.com/image/fetch/$s_!dfip!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0c1e1d-f181-4c7e-acca-aae3816bca21_3030x210.png 848w, https://substackcdn.com/image/fetch/$s_!dfip!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0c1e1d-f181-4c7e-acca-aae3816bca21_3030x210.png 1272w, https://substackcdn.com/image/fetch/$s_!dfip!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0c1e1d-f181-4c7e-acca-aae3816bca21_3030x210.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dfip!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0c1e1d-f181-4c7e-acca-aae3816bca21_3030x210.png" width="1456" height="101" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5d0c1e1d-f181-4c7e-acca-aae3816bca21_3030x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:101,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:43133,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0c1e1d-f181-4c7e-acca-aae3816bca21_3030x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!dfip!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0c1e1d-f181-4c7e-acca-aae3816bca21_3030x210.png 424w, https://substackcdn.com/image/fetch/$s_!dfip!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0c1e1d-f181-4c7e-acca-aae3816bca21_3030x210.png 848w, https://substackcdn.com/image/fetch/$s_!dfip!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0c1e1d-f181-4c7e-acca-aae3816bca21_3030x210.png 1272w, https://substackcdn.com/image/fetch/$s_!dfip!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0c1e1d-f181-4c7e-acca-aae3816bca21_3030x210.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Output is what the team produces. Throughput is what successfully flows through the system. Outcome is what matters after users touch it.</p><p>A pull request is output. A merged pull request is still not necessarily outcome. A deployed change is closer, but even deployment is not enough if the change increases incidents, damages trust, or creates future drag. The system goal is not &#8220;more code&#8221;. The goal is valuable, safe, validated change.</p><p>That distinction sounds obvious until AI enters the room. AI makes the most visible station faster, so the organization starts treating that station as if it were the whole system. Dashboards celebrate accepted suggestions, generated lines, closed tickets, and merged PRs. The kitchen applauds the chopping machine while the pass fills with plates no one can safely send out.</p><p>AI lowers the cost of starting. But in a constrained system, the cost that matters is often the cost of finishing.</p><h2>The Faros data shows the bill arriving downstream</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DGDn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca178f1e-3c3f-4cd2-9ad7-7c31ee225bbd_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DGDn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca178f1e-3c3f-4cd2-9ad7-7c31ee225bbd_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!DGDn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca178f1e-3c3f-4cd2-9ad7-7c31ee225bbd_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!DGDn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca178f1e-3c3f-4cd2-9ad7-7c31ee225bbd_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!DGDn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca178f1e-3c3f-4cd2-9ad7-7c31ee225bbd_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DGDn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca178f1e-3c3f-4cd2-9ad7-7c31ee225bbd_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ca178f1e-3c3f-4cd2-9ad7-7c31ee225bbd_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:499866,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca178f1e-3c3f-4cd2-9ad7-7c31ee225bbd_1024x559.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_!DGDn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca178f1e-3c3f-4cd2-9ad7-7c31ee225bbd_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!DGDn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca178f1e-3c3f-4cd2-9ad7-7c31ee225bbd_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!DGDn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca178f1e-3c3f-4cd2-9ad7-7c31ee225bbd_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!DGDn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca178f1e-3c3f-4cd2-9ad7-7c31ee225bbd_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The Faros report is not just another opinion survey. It analyzes telemetry from task management systems, IDEs, static analysis tools, CI/CD pipelines, version control systems, incident management systems, and HR metadata. Faros standardized metrics per company, used Spearman rank correlation, reported statistically significant relationships with p-value below 0.05, required data from at least six companies, and compared each team&#8217;s two lowest AI-adoption quarters with its two highest AI-adoption quarters. The report also states an important limitation: the 2026 and 2025 datasets are independent cross-sections, so comparisons with the previous report are directional, not precise year-over-year longitudinal claims.</p><p>That matters because the pattern in the data is not simply &#8220;AI bad&#8221; or &#8220;AI good&#8221;. The pattern is more interesting: upstream output rises, then downstream absorption gets worse.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ENRk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ENRk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png 424w, https://substackcdn.com/image/fetch/$s_!ENRk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png 848w, https://substackcdn.com/image/fetch/$s_!ENRk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png 1272w, https://substackcdn.com/image/fetch/$s_!ENRk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ENRk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png" width="1456" height="1075" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1075,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:390640,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.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_!ENRk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png 424w, https://substackcdn.com/image/fetch/$s_!ENRk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png 848w, https://substackcdn.com/image/fetch/$s_!ENRk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png 1272w, https://substackcdn.com/image/fetch/$s_!ENRk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45d50e49-770b-4ff3-80c2-2ba8e31d5793_1510x1115.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>First, AI increased upstream output. Faros reports task throughput per developer up 33.7%, epics completed per developer up 66.2%, PR merge rate per developer up 16.2%, and tasks completed per team with an associated PR up 210%. Those numbers show acceleration, especially around code-related work.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vr_C!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8488c4fb-0d5b-47c9-b3f5-9b7092be6e8c_1487x1104.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vr_C!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8488c4fb-0d5b-47c9-b3f5-9b7092be6e8c_1487x1104.png 424w, https://substackcdn.com/image/fetch/$s_!vr_C!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8488c4fb-0d5b-47c9-b3f5-9b7092be6e8c_1487x1104.png 848w, https://substackcdn.com/image/fetch/$s_!vr_C!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8488c4fb-0d5b-47c9-b3f5-9b7092be6e8c_1487x1104.png 1272w, https://substackcdn.com/image/fetch/$s_!vr_C!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8488c4fb-0d5b-47c9-b3f5-9b7092be6e8c_1487x1104.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vr_C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8488c4fb-0d5b-47c9-b3f5-9b7092be6e8c_1487x1104.png" width="1456" height="1081" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8488c4fb-0d5b-47c9-b3f5-9b7092be6e8c_1487x1104.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1081,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:261658,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8488c4fb-0d5b-47c9-b3f5-9b7092be6e8c_1487x1104.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_!vr_C!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8488c4fb-0d5b-47c9-b3f5-9b7092be6e8c_1487x1104.png 424w, https://substackcdn.com/image/fetch/$s_!vr_C!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8488c4fb-0d5b-47c9-b3f5-9b7092be6e8c_1487x1104.png 848w, https://substackcdn.com/image/fetch/$s_!vr_C!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8488c4fb-0d5b-47c9-b3f5-9b7092be6e8c_1487x1104.png 1272w, https://substackcdn.com/image/fetch/$s_!vr_C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8488c4fb-0d5b-47c9-b3f5-9b7092be6e8c_1487x1104.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Second, the work became larger and harder to absorb. Faros reports average PR size up 51.3%, average files edited per PR up 59.7%, files touched per developer per month up 149.9%, and repositories touched per developer per month up 11.7%. In plain English, AI-assisted work did not merely arrive faster. It arrived in larger packages, spread across more of the codebase.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fZu0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c37f0e6-8467-4d55-9333-fcb397a672a5_1490x1090.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fZu0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c37f0e6-8467-4d55-9333-fcb397a672a5_1490x1090.png 424w, https://substackcdn.com/image/fetch/$s_!fZu0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c37f0e6-8467-4d55-9333-fcb397a672a5_1490x1090.png 848w, https://substackcdn.com/image/fetch/$s_!fZu0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c37f0e6-8467-4d55-9333-fcb397a672a5_1490x1090.png 1272w, https://substackcdn.com/image/fetch/$s_!fZu0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c37f0e6-8467-4d55-9333-fcb397a672a5_1490x1090.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fZu0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c37f0e6-8467-4d55-9333-fcb397a672a5_1490x1090.png" width="1456" height="1065" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5c37f0e6-8467-4d55-9333-fcb397a672a5_1490x1090.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1065,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:215352,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c37f0e6-8467-4d55-9333-fcb397a672a5_1490x1090.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_!fZu0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c37f0e6-8467-4d55-9333-fcb397a672a5_1490x1090.png 424w, https://substackcdn.com/image/fetch/$s_!fZu0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c37f0e6-8467-4d55-9333-fcb397a672a5_1490x1090.png 848w, https://substackcdn.com/image/fetch/$s_!fZu0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c37f0e6-8467-4d55-9333-fcb397a672a5_1490x1090.png 1272w, https://substackcdn.com/image/fetch/$s_!fZu0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c37f0e6-8467-4d55-9333-fcb397a672a5_1490x1090.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Third, review absorbed the shock. Faros reports review comments per PR up 25%, average review comment length up 22.7%, and PRs merged without any review up 31.3%. The report includes sensible caveats: larger PRs can naturally produce more comments, and AI review agents may inflate comment volume. But the workflow timing data points in the same direction: median time to first PR review increased 156.6%, average time in PR review increased 199.6%, and median time in PR review increased 441.5%.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_1Og!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bf83bb-10a4-47ed-b182-2ea647944210_1497x1103.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_1Og!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bf83bb-10a4-47ed-b182-2ea647944210_1497x1103.png 424w, https://substackcdn.com/image/fetch/$s_!_1Og!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bf83bb-10a4-47ed-b182-2ea647944210_1497x1103.png 848w, https://substackcdn.com/image/fetch/$s_!_1Og!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bf83bb-10a4-47ed-b182-2ea647944210_1497x1103.png 1272w, https://substackcdn.com/image/fetch/$s_!_1Og!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bf83bb-10a4-47ed-b182-2ea647944210_1497x1103.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_1Og!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bf83bb-10a4-47ed-b182-2ea647944210_1497x1103.png" width="1456" height="1073" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/40bf83bb-10a4-47ed-b182-2ea647944210_1497x1103.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1073,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:231035,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bf83bb-10a4-47ed-b182-2ea647944210_1497x1103.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_!_1Og!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bf83bb-10a4-47ed-b182-2ea647944210_1497x1103.png 424w, https://substackcdn.com/image/fetch/$s_!_1Og!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bf83bb-10a4-47ed-b182-2ea647944210_1497x1103.png 848w, https://substackcdn.com/image/fetch/$s_!_1Og!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bf83bb-10a4-47ed-b182-2ea647944210_1497x1103.png 1272w, https://substackcdn.com/image/fetch/$s_!_1Og!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bf83bb-10a4-47ed-b182-2ea647944210_1497x1103.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Fourth, the system slowed downstream. Faros reports average time a task spends in progress up 225.2% and average waiting time up 81.8%. For the subset of organizations that instrumented lead time from commit to production, approximately 10% of the dataset, Faros reports lead time up 480.4%. The report itself warns that this figure has high variance and should be treated directionally, but the direction fits the wider pattern: code is completing, but it is not reaching production faster.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Izil!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fe2d1ca-c3b1-4bd8-9242-177d872fa4fe_1492x926.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Izil!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fe2d1ca-c3b1-4bd8-9242-177d872fa4fe_1492x926.png 424w, https://substackcdn.com/image/fetch/$s_!Izil!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fe2d1ca-c3b1-4bd8-9242-177d872fa4fe_1492x926.png 848w, https://substackcdn.com/image/fetch/$s_!Izil!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fe2d1ca-c3b1-4bd8-9242-177d872fa4fe_1492x926.png 1272w, https://substackcdn.com/image/fetch/$s_!Izil!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fe2d1ca-c3b1-4bd8-9242-177d872fa4fe_1492x926.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Izil!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fe2d1ca-c3b1-4bd8-9242-177d872fa4fe_1492x926.png" width="1456" height="904" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7fe2d1ca-c3b1-4bd8-9242-177d872fa4fe_1492x926.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:904,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:226695,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fe2d1ca-c3b1-4bd8-9242-177d872fa4fe_1492x926.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_!Izil!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fe2d1ca-c3b1-4bd8-9242-177d872fa4fe_1492x926.png 424w, https://substackcdn.com/image/fetch/$s_!Izil!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fe2d1ca-c3b1-4bd8-9242-177d872fa4fe_1492x926.png 848w, https://substackcdn.com/image/fetch/$s_!Izil!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fe2d1ca-c3b1-4bd8-9242-177d872fa4fe_1492x926.png 1272w, https://substackcdn.com/image/fetch/$s_!Izil!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fe2d1ca-c3b1-4bd8-9242-177d872fa4fe_1492x926.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Fifth, production paid the bill. Faros reports incidents per PR up 242.7%, monthly incidents up 57.9%, bugs per developer up 54%, bugs per PR up 28.7%, and reopened Jira tickets up 12.6%. The report is careful about one unresolved question: future analysis should examine whether the increase in bugs and incidents persists after normalizing for PR size, or whether larger PRs explain much of the deterioration. That caveat matters. But even with it, the signal is serious.</p><p>The numbers are not the story. The movement of pressure is the story.</p><p><strong>Loopy diagram: <a href="https://ncase.me/loopy/v1.1/?data=[[[1,502,193,0,%22AI%2520coding%2520rate%22,4],[2,996,196,0,%22PR%2520volume%22,5],[3,1174,366,0,%22Review%2520load%22,1],[4,1184,647,1,%22Review%2520quality%22,0],[5,492,621,0,%22Escaped%2520defects%22,2],[6,503,445,0,%22Rework%22,3],[7,780,480,0,%22Delivery%2520pressure%22,4]],[[1,2,50,1,0],[2,3,50,1,0],[3,7,50,1,0],[7,1,50,1,0],[3,4,50,-1,0],[4,5,50,-1,0],[5,6,50,1,0],[6,2,50,1,0]],[[473,79,%22Click%2520%25E2%2586%2591%2520on%2520AI%2520coding%2520rate.%250AMore%2520generated%2520work%2520creates%2520more%2520PRs.%22],[989,102,%22More%2520PRs%2520increase%2520review%2520load.%22],[452,747,%22High%2520review%2520load%2520creates%2520delivery%2520pressure%252C%250Awhich%2520often%2520pushes%2520teams%2520to%2520use%2520even%2520more%2520AI.%22],[1208,748,%22Heavy%2520review%2520load%2520lowers%2520review%2520quality.%250ALower%2520review%2520quality%2520lets%2520defects%2520escape.%22],[1207,778,%22%22],[346,350,%22Escaped%2520defects%2520create%2520rework.%250ARework%2520comes%2520back%2520as%2520more%2520PR%2520volume.%22]],1%5D">AI acceleration whiplash</a></strong></p><p>This first Loopy model shows two connected vicious cycles. More AI-assisted coding creates more PR volume. More PR volume increases review load. High review load creates delivery pressure, which often pushes teams to use AI even more aggressively. At the same time, heavy review load reduces review quality, defects escape, and rework comes back as more PR volume.</p><p>The pass fills up, so the kitchen tries to chop faster.</p><p>Faros does not prove that AI mechanically causes every downstream failure in every organization. It shows a telemetry pattern that systems thinkers should take seriously: as AI adoption rises, upstream output rises, and several downstream absorption and quality signals worsen.</p><p>That is enough to ask a better question.</p><p>Not: is AI making developers faster?</p><p>The better question is: where is the system actually constrained?</p><h2>This is not a contradiction. It is suboptimization</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0fYr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39293853-b660-45f0-bbd8-0bdfe7b39c4a_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0fYr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39293853-b660-45f0-bbd8-0bdfe7b39c4a_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!0fYr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39293853-b660-45f0-bbd8-0bdfe7b39c4a_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!0fYr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39293853-b660-45f0-bbd8-0bdfe7b39c4a_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!0fYr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39293853-b660-45f0-bbd8-0bdfe7b39c4a_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0fYr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39293853-b660-45f0-bbd8-0bdfe7b39c4a_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/39293853-b660-45f0-bbd8-0bdfe7b39c4a_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:882435,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39293853-b660-45f0-bbd8-0bdfe7b39c4a_1024x558.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_!0fYr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39293853-b660-45f0-bbd8-0bdfe7b39c4a_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!0fYr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39293853-b660-45f0-bbd8-0bdfe7b39c4a_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!0fYr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39293853-b660-45f0-bbd8-0bdfe7b39c4a_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!0fYr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39293853-b660-45f0-bbd8-0bdfe7b39c4a_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Suboptimization happens when a part of the system improves in a way that does not improve the whole. Sometimes it merely fails to help. Sometimes it actively makes the system worse by increasing pressure on the true constraint.</p><p>This is why the AI productivity debate can feel contradictory. Developers can feel faster because the local act of writing, editing, and exploring code has become easier. Managers can see more tasks completed because local output has increased. Yet reviewers can be drowning, QA can be slower, production can be less stable, and customers can see more defects.</p><p>All of those things can be true at the same time.</p><p>DORA&#8217;s recent findings help explain part of the tension. The 2025 DORA report frames AI as an amplifier: it magnifies an organization&#8217;s existing strengths and weaknesses, and the strongest returns come from improving the underlying organizational system, not from the tools alone. That is a systems claim, not a tooling claim.</p><p>This is also where Faros and DORA diverge. DORA frames AI as an amplifier of organizational strengths and weaknesses, while Faros reports that even organizations with strong pre-AI engineering performance, mature DevOps practices, high DORA scores, and disciplined delivery processes were not insulated from downstream deterioration. These claims are not necessarily incompatible. A strong system can still be overloaded if one station accelerates faster than the constraint can adapt.</p><p>DORA&#8217;s 2024 research also warned that AI adoption had tradeoffs. Google&#8217;s DORA summary states that a 25% increase in AI adoption was associated with an estimated 1.5% decrease in delivery throughput and a 7.2% decrease in delivery stability, while still reporting improvements to parts of the development process. The important lesson is not that AI is harmful by default. The lesson is that improving development activity does not automatically improve software delivery performance.</p><p>That sentence should make engineering leaders pause.</p><p>Software delivery is not a typing contest. It is a constraint-governed system. If we optimize the non-constraint, we may only produce more pressure for the constraint to absorb.</p><p>The kitchen did not fail because the chopping machine was bad. It failed because the restaurant treated faster chopping as faster dinner.</p><h2>Strong foundations are not immunity</h2><p>This is where the Faros takeaways become uncomfortable.</p><p>DORA 2025 frames AI as an amplifier: organizations with stronger systems should be better positioned to benefit from AI and avoid some of its downsides. That is a reasonable hypothesis. Good tests, CI/CD, observability, small batches, and disciplined delivery should matter.</p><p>Faros&#8217; telemetry adds a harsher correction. Its tenth-takeaways article says high-performing engineering organizations, including those with mature DevOps practices, high DORA metrics, and disciplined delivery processes, experienced the same downstream deterioration as everyone else.</p><p>Strong foundations do not repeal system dynamics.</p><p>They increase capacity. They improve feedback. They make problems easier to see. But they do not create infinite absorption. If AI increases the arrival rate of work faster than review, testing, deployment, and production learning can absorb it, even a mature system can overload.</p><p>This is not a contradiction between DORA and Faros as much as a difference in lens. Survey data can capture perceived productivity, confidence, and adoption. Telemetry can reveal what happens later in the system: queues, rework, incidents, unreviewed merges, and bugs reaching customers. Faros makes that distinction explicitly when contrasting survey perception with engineering-system telemetry.</p><p>The lesson is not &#8220;foundations do not matter&#8221;. They matter enormously. The lesson is sharper: foundations are not armor. They are capacity. And capacity can be exceeded.</p><p>Maturity is not immunity. It is capacity under conditions.</p><p>A system that was healthy at one arrival rate can become fragile at another.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!R906!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171bbe15-743e-448f-8554-3b3291d7c708_983x384.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!R906!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171bbe15-743e-448f-8554-3b3291d7c708_983x384.png 424w, https://substackcdn.com/image/fetch/$s_!R906!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171bbe15-743e-448f-8554-3b3291d7c708_983x384.png 848w, https://substackcdn.com/image/fetch/$s_!R906!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171bbe15-743e-448f-8554-3b3291d7c708_983x384.png 1272w, https://substackcdn.com/image/fetch/$s_!R906!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171bbe15-743e-448f-8554-3b3291d7c708_983x384.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!R906!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171bbe15-743e-448f-8554-3b3291d7c708_983x384.png" width="983" height="384" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/171bbe15-743e-448f-8554-3b3291d7c708_983x384.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:384,&quot;width&quot;:983,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:113990,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171bbe15-743e-448f-8554-3b3291d7c708_983x384.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_!R906!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171bbe15-743e-448f-8554-3b3291d7c708_983x384.png 424w, https://substackcdn.com/image/fetch/$s_!R906!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171bbe15-743e-448f-8554-3b3291d7c708_983x384.png 848w, https://substackcdn.com/image/fetch/$s_!R906!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171bbe15-743e-448f-8554-3b3291d7c708_983x384.png 1272w, https://substackcdn.com/image/fetch/$s_!R906!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F171bbe15-743e-448f-8554-3b3291d7c708_983x384.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Code generation was not the constraint</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0XZF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e060647-0ab0-4eda-b338-82c43738d781_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0XZF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e060647-0ab0-4eda-b338-82c43738d781_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!0XZF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e060647-0ab0-4eda-b338-82c43738d781_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!0XZF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e060647-0ab0-4eda-b338-82c43738d781_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!0XZF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e060647-0ab0-4eda-b338-82c43738d781_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0XZF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e060647-0ab0-4eda-b338-82c43738d781_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0e060647-0ab0-4eda-b338-82c43738d781_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:634710,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e060647-0ab0-4eda-b338-82c43738d781_1024x559.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_!0XZF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e060647-0ab0-4eda-b338-82c43738d781_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!0XZF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e060647-0ab0-4eda-b338-82c43738d781_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!0XZF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e060647-0ab0-4eda-b338-82c43738d781_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!0XZF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e060647-0ab0-4eda-b338-82c43738d781_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Theory of Constraints</em> (TOC) starts with a hard question: what limits the system from achieving more of its goal right now?</p><p>The classic five focusing steps are:</p><ol><li><p>Identify the system constraint.</p></li><li><p>Exploit the constraint using what already exists.</p></li><li><p>Subordinate everything else to the constraint.</p></li><li><p>Elevate the constraint if it still limits the system.</p></li><li><p>Repeat when the constraint moves, and avoid inertia.</p></li></ol><p>TOC is explicit about the managerial move: identify the system constraint and improve that constraint, rather than optimizing every resource independently.</p><p>That is exactly the risk with AI-generated code.</p><p>It doesn&#8217;t matter how your workflow would look like</p><p>Option 1:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!muiC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab239b8-5c37-4602-8fb7-aaa7421ba191_2555x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!muiC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab239b8-5c37-4602-8fb7-aaa7421ba191_2555x140.png 424w, https://substackcdn.com/image/fetch/$s_!muiC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab239b8-5c37-4602-8fb7-aaa7421ba191_2555x140.png 848w, https://substackcdn.com/image/fetch/$s_!muiC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab239b8-5c37-4602-8fb7-aaa7421ba191_2555x140.png 1272w, https://substackcdn.com/image/fetch/$s_!muiC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab239b8-5c37-4602-8fb7-aaa7421ba191_2555x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!muiC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab239b8-5c37-4602-8fb7-aaa7421ba191_2555x140.png" width="1456" height="80" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0ab239b8-5c37-4602-8fb7-aaa7421ba191_2555x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:80,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29419,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab239b8-5c37-4602-8fb7-aaa7421ba191_2555x140.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_!muiC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab239b8-5c37-4602-8fb7-aaa7421ba191_2555x140.png 424w, https://substackcdn.com/image/fetch/$s_!muiC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab239b8-5c37-4602-8fb7-aaa7421ba191_2555x140.png 848w, https://substackcdn.com/image/fetch/$s_!muiC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab239b8-5c37-4602-8fb7-aaa7421ba191_2555x140.png 1272w, https://substackcdn.com/image/fetch/$s_!muiC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ab239b8-5c37-4602-8fb7-aaa7421ba191_2555x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Option 2:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XsBF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3471f58b-9a9e-4c70-8788-4aaabc472c71_2555x140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XsBF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3471f58b-9a9e-4c70-8788-4aaabc472c71_2555x140.png 424w, https://substackcdn.com/image/fetch/$s_!XsBF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3471f58b-9a9e-4c70-8788-4aaabc472c71_2555x140.png 848w, https://substackcdn.com/image/fetch/$s_!XsBF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3471f58b-9a9e-4c70-8788-4aaabc472c71_2555x140.png 1272w, https://substackcdn.com/image/fetch/$s_!XsBF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3471f58b-9a9e-4c70-8788-4aaabc472c71_2555x140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XsBF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3471f58b-9a9e-4c70-8788-4aaabc472c71_2555x140.png" width="1456" height="80" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3471f58b-9a9e-4c70-8788-4aaabc472c71_2555x140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:80,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29974,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3471f58b-9a9e-4c70-8788-4aaabc472c71_2555x140.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_!XsBF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3471f58b-9a9e-4c70-8788-4aaabc472c71_2555x140.png 424w, https://substackcdn.com/image/fetch/$s_!XsBF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3471f58b-9a9e-4c70-8788-4aaabc472c71_2555x140.png 848w, https://substackcdn.com/image/fetch/$s_!XsBF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3471f58b-9a9e-4c70-8788-4aaabc472c71_2555x140.png 1272w, https://substackcdn.com/image/fetch/$s_!XsBF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3471f58b-9a9e-4c70-8788-4aaabc472c71_2555x140.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Option 3:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bDXS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ad30b4-de1f-4f67-8193-ba0f1123d345_3030x210.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bDXS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ad30b4-de1f-4f67-8193-ba0f1123d345_3030x210.png 424w, https://substackcdn.com/image/fetch/$s_!bDXS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ad30b4-de1f-4f67-8193-ba0f1123d345_3030x210.png 848w, https://substackcdn.com/image/fetch/$s_!bDXS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ad30b4-de1f-4f67-8193-ba0f1123d345_3030x210.png 1272w, https://substackcdn.com/image/fetch/$s_!bDXS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ad30b4-de1f-4f67-8193-ba0f1123d345_3030x210.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bDXS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ad30b4-de1f-4f67-8193-ba0f1123d345_3030x210.png" width="1456" height="101" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d3ad30b4-de1f-4f67-8193-ba0f1123d345_3030x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:101,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:44320,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ad30b4-de1f-4f67-8193-ba0f1123d345_3030x210.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_!bDXS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ad30b4-de1f-4f67-8193-ba0f1123d345_3030x210.png 424w, https://substackcdn.com/image/fetch/$s_!bDXS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ad30b4-de1f-4f67-8193-ba0f1123d345_3030x210.png 848w, https://substackcdn.com/image/fetch/$s_!bDXS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ad30b4-de1f-4f67-8193-ba0f1123d345_3030x210.png 1272w, https://substackcdn.com/image/fetch/$s_!bDXS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ad30b4-de1f-4f67-8193-ba0f1123d345_3030x210.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The <strong>review</strong> step is where many AI strategies fail. Subordination means that non-constraints should not run at maximum local efficiency. They should run in a way that supports the constraint. A non-bottleneck producing at full speed may look efficient on its own dashboard while damaging the whole system.</p><p>That is exactly the risk with AI-generated code.</p><p>If review is the constraint, increasing code generation mainly increases the review queue. If production stability is the constraint, increasing merged changes mainly increases risk exposure. If architectural understanding is the constraint, AI can help produce plausible changes faster than the organization can reason about their consequences.</p><p>The bottleneck does not care who wrote the code.</p><p>In TOC terms, a lot of AI-generated code is not throughput. It is inventory.</p><p>A pull request is not value. It is a claim on future judgment.</p><h2>AI fed the bottleneck</h2><p>Once we look through TOC, the Faros data becomes easier to interpret.</p><p>AI made it cheaper to start work. Developers touched more PRs per day, switched across more task contexts, restarted more work, and left more in-progress tasks inactive for seven or more days. Faros reports daily PR contexts per developer up 67.4%, daily task contexts up 17.7%, work restarts up 13.8%, and in-progress tasks with no PR or activity in the previous seven days up 26%.</p><p>That is not just &#8220;more activity&#8221;. It is a system where starting has become cheaper than finishing.</p><p>The pass in the restaurant fills up. The head chef now has to inspect more plates, with more ingredients, prepared faster, by a machine that does not know which orders were misunderstood. Some plates are fine. Some are almost fine. Some look fine until the first customer takes a bite.</p><p>Faros describes AI-generated code as often superficially convincing: idiomatic, well-named, and stylistically consistent with the surrounding codebase. The failures, when present, may be structural or logical, which means reviewers must reason about intent, not just scan for obvious mistakes. Faros calls this slow, expensive cognitive work, and says it falls heavily on senior engineers.</p><p>That is how the bottleneck gets fed.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FLQZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d1e4f59-e439-4a64-98b7-6b38f99c4d35_3489x258.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FLQZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d1e4f59-e439-4a64-98b7-6b38f99c4d35_3489x258.png 424w, https://substackcdn.com/image/fetch/$s_!FLQZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d1e4f59-e439-4a64-98b7-6b38f99c4d35_3489x258.png 848w, https://substackcdn.com/image/fetch/$s_!FLQZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d1e4f59-e439-4a64-98b7-6b38f99c4d35_3489x258.png 1272w, https://substackcdn.com/image/fetch/$s_!FLQZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d1e4f59-e439-4a64-98b7-6b38f99c4d35_3489x258.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FLQZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d1e4f59-e439-4a64-98b7-6b38f99c4d35_3489x258.png" width="1456" height="108" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4d1e4f59-e439-4a64-98b7-6b38f99c4d35_3489x258.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:108,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59402,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d1e4f59-e439-4a64-98b7-6b38f99c4d35_3489x258.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_!FLQZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d1e4f59-e439-4a64-98b7-6b38f99c4d35_3489x258.png 424w, https://substackcdn.com/image/fetch/$s_!FLQZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d1e4f59-e439-4a64-98b7-6b38f99c4d35_3489x258.png 848w, https://substackcdn.com/image/fetch/$s_!FLQZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d1e4f59-e439-4a64-98b7-6b38f99c4d35_3489x258.png 1272w, https://substackcdn.com/image/fetch/$s_!FLQZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d1e4f59-e439-4a64-98b7-6b38f99c4d35_3489x258.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Review is not a cleaning service for generated code. It is a scarce decision point in the system.</p><p>When generated work arrives too broad, too large, too under-tested, or too detached from intent, review becomes the warehouse where upstream quality problems are stored. A gate only works when the gatekeeper is not buried.</p><p>The wrong response is to say: &#8220;Add more review.&#8221;</p><p>More review may be needed temporarily, but it treats the symptom. If the code arriving at review is not review-ready, the deeper problem is upstream. The authoring process is sending work to the constraint before the work deserves constraint attention.</p><p>AI did not merely increase output. It changed the shape of the queue.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;fea52504-61c4-42dc-a745-555dd971da7e&quot;,&quot;caption&quot;:&quot;Every time I say AI on this article I&#8217;m talking about LLM and agents, and also MCP, because all of this elements ARE NOT AI. But everybody call this AI&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;AI can write code. That was never the bottleneck.&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-03-12T08:01:35.936Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!2Lrh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4af82fcc-7cfc-46f1-9633-92aa91a929e8_2816x1536.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/the-real-bottleneck-was-never-code&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:190302623,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:9,&quot;comment_count&quot;:0,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>The system pushed back</h2><p>Donella Meadows gives us the language to explain why this keeps surprising us. In <em>Thinking in Systems</em>, she explains that a system is made of interrelated things that produce a characteristic pattern of behavior over time. She also argues that systems often generate their own behavior through their structure, rather than simply reacting in linear ways to external events.</p><p>That is why &#8220;AI writes code faster&#8221; is an incomplete statement. It tells us something about one element. It tells us much less about the interrelations.</p><p>What happens to review queues? What happens to QA? What happens to deployment frequency? What happens to incident response? What happens to senior engineers who now spend more time reconstructing the intent behind plausible code? What happens to the codebase after six months of larger PRs touching more files?</p><p>Meadows also argues that words are often insufficient for systems because prose unfolds linearly, while systems act in many directions at once. That is why Loopy diagrams matter here. They help show that AI is not pushing on one isolated lever. It is changing arrival rates, feedback loops, queues, and delays.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uSgL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F483218b5-f863-441c-833b-5e124f6a6919_3826x226.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uSgL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F483218b5-f863-441c-833b-5e124f6a6919_3826x226.png 424w, https://substackcdn.com/image/fetch/$s_!uSgL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F483218b5-f863-441c-833b-5e124f6a6919_3826x226.png 848w, https://substackcdn.com/image/fetch/$s_!uSgL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F483218b5-f863-441c-833b-5e124f6a6919_3826x226.png 1272w, https://substackcdn.com/image/fetch/$s_!uSgL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F483218b5-f863-441c-833b-5e124f6a6919_3826x226.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uSgL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F483218b5-f863-441c-833b-5e124f6a6919_3826x226.png" width="1456" height="86" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/483218b5-f863-441c-833b-5e124f6a6919_3826x226.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:86,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:64941,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F483218b5-f863-441c-833b-5e124f6a6919_3826x226.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_!uSgL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F483218b5-f863-441c-833b-5e124f6a6919_3826x226.png 424w, https://substackcdn.com/image/fetch/$s_!uSgL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F483218b5-f863-441c-833b-5e124f6a6919_3826x226.png 848w, https://substackcdn.com/image/fetch/$s_!uSgL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F483218b5-f863-441c-833b-5e124f6a6919_3826x226.png 1272w, https://substackcdn.com/image/fetch/$s_!uSgL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F483218b5-f863-441c-833b-5e124f6a6919_3826x226.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The system pushes back through delay. Feedback arrives later. Wrong work survives longer. Rework consumes the same people needed for review. The organization becomes busier, but not necessarily more effective.</p><p>Little&#8217;s Law gives this intuition a mathematical backbone. In queueing theory, the long-term average number of items in a stable system equals the average arrival rate multiplied by the average time an item spends in the system. Expressed as L = &#955;W, it is not a perfect model of messy software organizations, but it gives a useful warning: if more work enters a constrained system and exit capacity does not rise with it, WIP or waiting time tends to grow. ([Wikipedia][6])</p><p>That is the Faros pattern in systems language.</p><p>AI increased arrival rate. The downstream system did not absorb it cleanly. WIP, waiting, review time, rework, and production quality signals moved in the wrong direction.</p><p>Starting work is local. Finishing work is systemic.</p><h2>The scarce resource is judgment</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Oygt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780bc688-7e1b-4461-b0e6-a988a5372754_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Oygt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780bc688-7e1b-4461-b0e6-a988a5372754_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Oygt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780bc688-7e1b-4461-b0e6-a988a5372754_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Oygt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780bc688-7e1b-4461-b0e6-a988a5372754_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Oygt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780bc688-7e1b-4461-b0e6-a988a5372754_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Oygt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780bc688-7e1b-4461-b0e6-a988a5372754_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/780bc688-7e1b-4461-b0e6-a988a5372754_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:669118,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780bc688-7e1b-4461-b0e6-a988a5372754_1024x559.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_!Oygt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780bc688-7e1b-4461-b0e6-a988a5372754_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Oygt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780bc688-7e1b-4461-b0e6-a988a5372754_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Oygt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780bc688-7e1b-4461-b0e6-a988a5372754_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Oygt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F780bc688-7e1b-4461-b0e6-a988a5372754_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For years, many organizations behaved as if the bottleneck in software delivery was typing. Developers were expensive, code took time, and the visible work was the writing of software. So when AI made code cheaper to generate, the conclusion seemed obvious: now we can do more.</p><p>But software was rarely constrained by typing.</p><p>The scarce resource was usually judgment: deciding what matters, understanding the system, preserving design options, validating behavior, reviewing risk, operating safely, and learning from production. AI changes the economics of code generation, but it does not remove the economics of comprehension.</p><p>Judgment is knowing when the generated solution solves the wrong problem. It is knowing when code fits the syntax of the architecture but violates the model. It is knowing whether a test protects behavior or merely freezes implementation. It is knowing when a small-looking change has a large blast radius. It is knowing when the AI has produced something plausible enough to pass a shallow review, but wrong enough to damage the system later.</p><p>The bottleneck is not review as a calendar event. It is the scarce ability to decide whether a change is correct, valuable, safe, reversible, and coherent with the system we intend to preserve.</p><p>This is why trust becomes the new bottleneck.</p><p>Trust is not a mood. Trust is an operational property. A change becomes trustworthy when it is small enough to understand, specified enough to test, aligned enough with architecture, safe enough to deploy, observable enough to monitor, and reversible enough to recover from.</p><p>When code becomes cheap to generate, trust becomes expensive to establish.</p><p>That is why the next generation of engineering performance will not be defined by who generates the most code. It will be defined by who can create the highest rate of trustworthy change.</p><h2>Cheap code is not a cheap asset</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8a3e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2764503-de59-470c-a829-c21a449a3382_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8a3e!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2764503-de59-470c-a829-c21a449a3382_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!8a3e!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2764503-de59-470c-a829-c21a449a3382_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!8a3e!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2764503-de59-470c-a829-c21a449a3382_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!8a3e!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2764503-de59-470c-a829-c21a449a3382_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8a3e!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2764503-de59-470c-a829-c21a449a3382_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2764503-de59-470c-a829-c21a449a3382_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:527453,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2764503-de59-470c-a829-c21a449a3382_1024x559.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_!8a3e!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2764503-de59-470c-a829-c21a449a3382_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!8a3e!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2764503-de59-470c-a829-c21a449a3382_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!8a3e!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2764503-de59-470c-a829-c21a449a3382_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!8a3e!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2764503-de59-470c-a829-c21a449a3382_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Cheap code is not automatically cheap software.</p><p>Cheap code can still create expensive obligations.</p><p>Software economics forces us to look beyond the cost of creation. A change has a build cost, but it also has basal cost, carrying cost, future change cost, cost of delay, risk, optionality, and cognitive load. AI may reduce part of the build cost while increasing several of the others.</p><p>It may also create a new coordination cost: the cost of managing the AI system itself.</p><p>A useful conceptual model is:</p><pre><code><code>Real cost &#8776; build cost
          + basal cost
          + carrying cost
          + future change cost
          + rework cost
          + incident cost
          + cost of delay
          + AI supervision cost</code></code></pre><p>This is not an accounting equation. It is a discipline for not lying to ourselves.</p><p>This matters because accounting and economics do not ask exactly the same question. In accounting terms, some software can be treated as an intangible asset when it meets the relevant recognition criteria. IAS 38 states that when software is not an integral part of the related hardware, computer software is treated as an intangible asset. FASB also has guidance for software costs that fall under internal-use software accounting.</p><p>But this article is not making an accounting claim. It is making an economic one.</p><p>Software starts as an economic liability, not an asset, until it proves that the value it creates exceeds the cost, risk, and attention it permanently consumes.</p><p>Every line that enters the product consumes future attention. It must be read, tested, secured, operated, debugged, migrated, deleted, or understood by someone later. It occupies cognitive space. It increases the surface area of change. It creates obligations for people who were not present when it was written.</p><p>Software becomes an economic asset only when the capability it creates is worth more than the permanent cost of carrying it. Until then, it is not value. It is a claim on future money, time, attention, and judgment.</p><p><strong>Loopy diagram: <a href="https://ncase.me/loopy/v1.1/?data=%5B%5B%5B1,577,329,0,%22Generated%2520code%2520volume%22,4%5D,%5B2,907,389,0,%22Software%2520liability%22,5%5D,%5B3,1087,629,0,%22Carrying%2520cost%22,1%5D,%5B4,877,849,0,%22Cognitive%2520load%22,0%5D,%5B5,547,809,1,%22Future%2520change%2520speed%22,2%5D,%5B6,417,559,0,%22Delivery%2520pressure%22,3%5D%5D,%5B%5B1,2,50,1,0%5D,%5B2,3,50,1,0%5D,%5B3,4,50,1,0%5D,%5B4,5,50,-1,0%5D,%5B5,6,50,-1,0%5D,%5B6,1,50,1,0%5D%5D,%5B%5B720,158,%22Click%2520%25E2%2586%2591%2520on%2520generated%2520code%2520volume.%250ACheap%2520code%2520first%2520becomes%2520something%2520the%2520system%2520must%2520carry.%22%5D,%5B971,293,%22More%2520software%2520liability%2520increases%2520carrying%2520cost.%22%5D,%5B886,955,%22Higher%2520cognitive%2520load%2520slows%2520future%2520change.%22%5D,%5B289,677,%22When%2520future%2520change%2520slows%252C%250Adelivery%2520pressure%2520rises.%22%5D,%5B445,259,%22More%2520pressure%2520often%2520creates%2520more%2520generated%2520code%252C%250Aclosing%2520the%2520vicious%2520loop.%22%5D%5D,1%5D">Software liability loop</a></strong></p><p>This second Loopy model shows the economic vicious cycle. More generated code increases software liability. More liability increases carrying cost. Carrying cost increases cognitive load. Cognitive load slows future change. Slower future change creates delivery pressure. Delivery pressure pushes the team to generate even more code.</p><p>The organization tries to escape the cost of software by producing more software.</p><p>This is why AI-generated code can be economically dangerous even when it is locally cheap. It reduces the cost of producing the liability, but not necessarily the cost of carrying it. If AI helps create more code than the organization can understand, validate, operate, and evolve, it has not created assets faster. It has created liabilities faster.</p><p>The Faros data points toward this cost migration. Larger PRs increase blast radius. More files touched per PR increase reasoning cost. More review time increases senior engineering load. More incidents increase operating expense. More reopened tickets signal rework. More code churn raises the question of whether shipped code is surviving long enough to justify the celebration.</p><p>The economic question is not whether AI makes code cheaper to produce.</p><p>The economic question is whether AI makes valuable change cheaper to absorb.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;e09d8a45-bc86-4b77-afae-da60bdc3390c&quot;,&quot;caption&quot;:&quot;Imagine a team creates a tiny serverless function. Just one Lambda. It receives a request, performs a small operation, and returns a response. The implementation is simple, the deployment is quick, and the infrastructure looks almost free. Nobody is worried because the unit cost is ridiculous.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Cheap Code is not cheap Software the SaaSmagedon&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:16266845,&quot;name&quot;:&quot;Emmanuel Valverde Ramos&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-05-07T05:01:36.741Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!RNtA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b4a59bd-9600-45d6-accd-0dd2fb0df258_1024x558.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://emmanuelvalverderamos.substack.com/p/cheap-code-is-not-cheap-software&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:196508702,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:3378739,&quot;publication_name&quot;:&quot;Crafting software&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!pJED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14c10479-27d7-4921-bafb-d5907f34d74e_396x396.jpeg&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>What leaders should measure now</h2><p>A metric system that celebrates generated code will reward congestion. A metric system that celebrates accepted suggestions will reward trust without evidence. A metric system that celebrates closed tickets without downstream signals will confuse output with outcome.</p><p>A better measurement system begins with four questions.</p><p>First: is value flowing?</p><p>Measure lead time from idea to production, lead time from commit to production, deployment frequency, customer-visible outcomes, and whether completed work actually reaches users. Faros reports that PR merge rate rose 16.2%, but deployments per week declined 11.7% in the measured subset. That is the shape of a system where local completion and production flow diverge.</p><p>Second: where is work accumulating?</p><p>Measure WIP, open PRs, stale tasks, waiting states, review queues, blocked items, and merged-but-unreleased work. If AI increases the rate of starting work faster than the system can finish it, WIP becomes the shadow inventory of the organization.</p><p>Third: what is coming back?</p><p>Measure rework, reopened tickets, recent code deletion, bugs per PR, incidents per PR, escaped defects, change failure rate, failed deployment recovery time, and deployment rework rate. Work that returns is not merely a quality problem. It is evidence that the system accepted something before it was ready.</p><p>Fourth: who is absorbing the cost?</p><p>Measure senior review load, context switching, interruptions, daily PR contexts, daily task contexts, and time spent reconstructing intent. If AI shifts cost from generation to review, the burden often lands on the people with the deepest system knowledge. That is dangerous because those people are also needed for architecture, mentoring, incident response, and the decisions that protect future optionality.</p><p>Fifth: are the foundations still absorbing the new load?</p><p>Measure whether the practices that used to protect the system still work under AI-accelerated volume: review latency, test signal quality, flaky test rate, CI queue time, deployment stability, rollback frequency, escaped defects, and incident clustering after AI-heavy changes. A practice that worked at human-paced throughput may fail when the arrival rate changes.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-R0A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecb5a730-52c3-485e-bacc-8623c68d95d4_3032x652.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-R0A!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecb5a730-52c3-485e-bacc-8623c68d95d4_3032x652.png 424w, https://substackcdn.com/image/fetch/$s_!-R0A!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecb5a730-52c3-485e-bacc-8623c68d95d4_3032x652.png 848w, https://substackcdn.com/image/fetch/$s_!-R0A!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecb5a730-52c3-485e-bacc-8623c68d95d4_3032x652.png 1272w, https://substackcdn.com/image/fetch/$s_!-R0A!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecb5a730-52c3-485e-bacc-8623c68d95d4_3032x652.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-R0A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecb5a730-52c3-485e-bacc-8623c68d95d4_3032x652.png" width="1456" height="313" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ecb5a730-52c3-485e-bacc-8623c68d95d4_3032x652.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:313,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:133179,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecb5a730-52c3-485e-bacc-8623c68d95d4_3032x652.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_!-R0A!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecb5a730-52c3-485e-bacc-8623c68d95d4_3032x652.png 424w, https://substackcdn.com/image/fetch/$s_!-R0A!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecb5a730-52c3-485e-bacc-8623c68d95d4_3032x652.png 848w, https://substackcdn.com/image/fetch/$s_!-R0A!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecb5a730-52c3-485e-bacc-8623c68d95d4_3032x652.png 1272w, https://substackcdn.com/image/fetch/$s_!-R0A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecb5a730-52c3-485e-bacc-8623c68d95d4_3032x652.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Also measure context debt. If the same questions appear in review again and again, if agents repeatedly miss architectural boundaries, if tests are unclear, or if engineers must reconstruct intent from scratch, the problem is not only the generated code. The system is missing usable context. That missing context becomes review cost.</p><p>Also measure AI supervision cost: the time spent maintaining prompts, rules, context files, agent instructions, review scaffolding, corrective workflows, and quality gates that exist only because the tool cannot yet manage the work reliably by itself.</p><p>These metrics are not a new altar. They are instruments. Their job is to reveal the constraint.</p><p>Once the constraint is visible, the organization can decide whether AI is protecting it, exploiting it, elevating it, or flooding it.</p><h2>How to use AI without flooding the system</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qGcr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02588e5e-1693-4eb5-8e1d-89753a79edfc_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qGcr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02588e5e-1693-4eb5-8e1d-89753a79edfc_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!qGcr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02588e5e-1693-4eb5-8e1d-89753a79edfc_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!qGcr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02588e5e-1693-4eb5-8e1d-89753a79edfc_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!qGcr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02588e5e-1693-4eb5-8e1d-89753a79edfc_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qGcr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02588e5e-1693-4eb5-8e1d-89753a79edfc_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/02588e5e-1693-4eb5-8e1d-89753a79edfc_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:619330,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02588e5e-1693-4eb5-8e1d-89753a79edfc_1024x559.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_!qGcr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02588e5e-1693-4eb5-8e1d-89753a79edfc_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!qGcr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02588e5e-1693-4eb5-8e1d-89753a79edfc_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!qGcr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02588e5e-1693-4eb5-8e1d-89753a79edfc_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!qGcr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02588e5e-1693-4eb5-8e1d-89753a79edfc_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The worst response to this data is panic. The second worst is blind acceleration.</p><p>AI should not be banned from software delivery. It should be subordinated to the system&#8217;s constraint. That is the practical center of the argument.</p><p>AI is not the drum. The constraint is the drum. AI must follow the beat.</p><p>That changes how leaders should govern AI-assisted development. The question is no longer &#8220;How do we get more developers to use AI?&#8221; The question becomes &#8220;Where should AI operate so that the system constraint improves rather than drowns?&#8221;</p><p>Start by identifying the constraint. Look at where work waits, where quality escapes, where decisions queue, and where senior judgment is overloaded. If the constraint is review, use AI to improve authoring quality, prepare review context, generate risk summaries, and keep PRs small. Do not use it to spray larger PRs into an already overloaded queue.</p><p>There is one loop leaders should protect deliberately: the context loop. But we should be careful about what that loop can and cannot do.</p><p>AI-assisted work is only as good as the environment it works inside. Architecture decisions, testing conventions, domain language, coding standards, previous incidents, examples of good changes, and explicit boundaries are not documentation for later. They are part of the authoring system now.</p><p>Better context can reduce preventable mistakes. It can make the agent less blind. It can reduce some review pain. It can make repeated errors easier to turn into reusable rules, examples, tests, or constraints.</p><p>But there is a cost here that the industry often hides.</p><p>Context management is not free. Every architecture note, agent instruction, coding rule, example, test convention, memory file, prompt template, and workflow constraint has to be written, maintained, reviewed, updated, and kept aligned with the real system. When the tool does not manage this well, the burden moves to the team.</p><p>This is not a small detail. It means engineers spend time managing the tool instead of improving the product. They maintain prompts, rules, context files, agent workflows, retrieval hints, and guardrails because the tool is not reliable enough to carry the work by itself. The team becomes responsible not only for the software, but also for the scaffolding required to make the AI behave acceptably.</p><p>This is an opinionated economic reading, not a measured claim from the Faros report.</p><p>It is time away from the core business. It is attention diverted from product learning, architecture, customer problems, operations, and delivery. And because the context is never finished, the cost is not a one-time setup cost. It becomes carrying cost.</p><p>The tool promised leverage. But part of the leverage is paid back as supervision.</p><p>This is why context engineering should not be treated as free infrastructure. If an organization needs to constantly chase, correct, constrain, and feed the tool so that it produces acceptable work, then the tool is not only reducing cost. It is also creating a new operating expense.</p><p>But context is not magic.</p><p>Context reduces some mistakes. It does not remove the need for judgment.</p><p>Even with strong context, LLMs will still make mistakes. Context windows are limited. Retrieval can miss relevant information. Different CLIs and agents manage context differently. Some will preserve intent better than others. Some will drift, truncate, overfit to the wrong files, or produce plausible code that still needs human review.</p><p>So the goal is not blind trust. The goal is lower preventable rework.</p><p>Poor context creates more preventable mistakes. More mistakes increase review pain. More review pain leaves less capacity to maintain useful context. The loop degrades quietly.</p><p>The opposite is possible too. Better context reduces preventable mistakes. Less preventable rework lowers review pain. Lower review pain leaves more capacity to maintain context. But residual rework remains, because human judgment remains part of the system.</p><p>Alfredo Artiles describes a similar reinforcing documentation loop: when documentation becomes obsolete, trust in it falls, usage falls, and the motivation to keep it updated falls too. If documentation becomes useful and accessible, use increases and the habit of maintaining it becomes easier to sustain. The same structure applies to AI context, with one important difference: better context can reduce preventable mistakes, but it does not remove LLM error.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yMpG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75f66097-6d89-4ec7-92c6-4c18de9f9f47_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yMpG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75f66097-6d89-4ec7-92c6-4c18de9f9f47_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!yMpG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75f66097-6d89-4ec7-92c6-4c18de9f9f47_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!yMpG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75f66097-6d89-4ec7-92c6-4c18de9f9f47_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!yMpG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75f66097-6d89-4ec7-92c6-4c18de9f9f47_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yMpG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75f66097-6d89-4ec7-92c6-4c18de9f9f47_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/75f66097-6d89-4ec7-92c6-4c18de9f9f47_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:594201,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75f66097-6d89-4ec7-92c6-4c18de9f9f47_1024x559.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_!yMpG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75f66097-6d89-4ec7-92c6-4c18de9f9f47_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!yMpG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75f66097-6d89-4ec7-92c6-4c18de9f9f47_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!yMpG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75f66097-6d89-4ec7-92c6-4c18de9f9f47_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!yMpG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75f66097-6d89-4ec7-92c6-4c18de9f9f47_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Better context can reduce preventable mistakes, but agent variance and residual rework remain. Human judgment is still required.</p><p>Then protect the constraint from bad inputs. Generated code should not arrive at review without tests, context, scope explanation, risk notes, and evidence that basic checks have passed. If <em>Test-Driven Development</em> (TDD) is appropriate for the work, AI should help create behavior-focused tests before or during implementation, not merely produce implementation and ask humans to clean up afterward.</p><p>Next, limit batch size. AI does not feel review cost. Humans do. Production does. Customers do. Small batches protect flow, review precision, rollback, and learning. If an agent produces broad changes across many files or repositories, the default response should not be admiration. It should be suspicion until the scope is justified.</p><p>Then turn human review into system learning. Every serious human review comment should feed back into coding guidelines, architectural rules, prompt context, test strategy, or automated checks. If the same review comment appears repeatedly, the system is paying recurring operating expense for a preventable upstream failure.</p><p>Finally, use AI where it strengthens the constraint. AI can help search incident history before implementation. It can draft characterization tests before refactoring. It can summarize architectural decisions. It can detect risky PRs. It can prepare rollback plans. It can find stale WIP. It can identify repeated review comments. It can help reduce the cost of doing the disciplined thing.</p><p>That is very different from using AI to maximize code volume.</p><p>Stop measuring AI success by accepted suggestions. Stop celebrating PR volume without checking downstream flow. Stop letting agents create broad changes without explicit scope limits. Stop sending untested generated code to senior engineers as if review were a laundry service. Stop treating incidents as a production problem when many of them were authored upstream.</p><p>Do this instead: before scaling AI, find the constraint. Then ask whether AI will protect it, elevate it, or flood it.</p><p>If the answer is flood it, redesign the system before you celebrate the speed.</p><h2>Conclusions: AI is not the drum</h2><p>The next phase of AI adoption will not be won by the organizations that generate the most code. It will be won by the organizations that know where value actually gets stuck.</p><p>It will also not be won by organizations that assume yesterday&#8217;s engineering maturity automatically protects tomorrow&#8217;s AI-accelerated system.</p><p>That is the central lesson from joining the Faros data with systems thinking, <em>Theory of Constraints</em> (TOC), Loopy-style causal loops, and software economics. Faros shows real acceleration and real downstream degradation. DORA reminds us that AI acts through the organizational system, not outside it. METR reminds us that perceived speed and measured performance can diverge. TOC gives us the discipline to ask where the constraint is. Meadows gives us the humility to remember that systems push back through feedback loops, delays, and interrelations.</p><p>AI is not the villain. The villain is local optimization dressed up as transformation.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FT7-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d095123-cbc3-42d4-a0f9-74c999752f37_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FT7-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d095123-cbc3-42d4-a0f9-74c999752f37_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!FT7-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d095123-cbc3-42d4-a0f9-74c999752f37_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!FT7-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d095123-cbc3-42d4-a0f9-74c999752f37_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!FT7-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d095123-cbc3-42d4-a0f9-74c999752f37_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FT7-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d095123-cbc3-42d4-a0f9-74c999752f37_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5d095123-cbc3-42d4-a0f9-74c999752f37_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:678357,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d095123-cbc3-42d4-a0f9-74c999752f37_1024x558.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_!FT7-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d095123-cbc3-42d4-a0f9-74c999752f37_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!FT7-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d095123-cbc3-42d4-a0f9-74c999752f37_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!FT7-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d095123-cbc3-42d4-a0f9-74c999752f37_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!FT7-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d095123-cbc3-42d4-a0f9-74c999752f37_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We made code faster. That can be useful. But if the system cannot absorb, validate, operate, and evolve that code, we have not accelerated asset creation. We have accelerated liability creation.</p><p>The organizations that benefit most from AI will not be the ones with the most prompts. They will be the ones with the best mechanisms for reducing preventable rework while preserving human judgment.</p><p>Maturity is not immunity. It is capacity under conditions.</p><p>When the conditions change, the constraint must be found again.</p><p>The kitchen does not need the chopping machine to slow down. It needs the kitchen to stop pretending that chopping is dinner.</p><p>AI is not the drum.</p><p>The constraint is.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LEDb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d296616-2f13-495a-8199-cd58b8d37fcd_1024x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LEDb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d296616-2f13-495a-8199-cd58b8d37fcd_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!LEDb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d296616-2f13-495a-8199-cd58b8d37fcd_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!LEDb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d296616-2f13-495a-8199-cd58b8d37fcd_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!LEDb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d296616-2f13-495a-8199-cd58b8d37fcd_1024x558.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LEDb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d296616-2f13-495a-8199-cd58b8d37fcd_1024x558.png" width="1024" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d296616-2f13-495a-8199-cd58b8d37fcd_1024x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:761815,&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://emmanuelvalverderamos.substack.com/i/197182453?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d296616-2f13-495a-8199-cd58b8d37fcd_1024x558.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_!LEDb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d296616-2f13-495a-8199-cd58b8d37fcd_1024x558.png 424w, https://substackcdn.com/image/fetch/$s_!LEDb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d296616-2f13-495a-8199-cd58b8d37fcd_1024x558.png 848w, https://substackcdn.com/image/fetch/$s_!LEDb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d296616-2f13-495a-8199-cd58b8d37fcd_1024x558.png 1272w, https://substackcdn.com/image/fetch/$s_!LEDb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d296616-2f13-495a-8199-cd58b8d37fcd_1024x558.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>And when judgment becomes the constraint, flooding it is not acceleration. It is the productivity trap.</p><h2>References</h2><ul><li><p>Faros Research. <em>AI Engineering Report 2026: The Acceleration Whiplash</em>. <a href="https://www.faros.ai/research/ai-acceleration-whiplash">https://www.faros.ai/research/ai-acceleration-whiplash</a></p></li><li><p>Faros Research. <em>Ten takeaways from the AI Engineering Report 2026: The Acceleration Whiplash</em>. <a href="https://www.faros.ai/blog/ai-acceleration-whiplash-takeaways?utm_source=chatgpt.com">https://www.faros.ai/blog/ai-acceleration-whiplash-takeaways</a></p></li><li><p>Meadows, Donella. <em>Thinking in Systems: A Primer</em>. Chelsea Green Publishing, 2008. <a href="https://www.chelseagreen.com/product/thinking-in-systems/">https://www.chelseagreen.com/product/thinking-in-systems/</a></p></li><li><p>Goldratt, Eliyahu M. <em>The Goal: A Process of Ongoing Improvement</em>. North River Press, 1984.</p></li><li><p>Goldratt, Eliyahu M. <em>The Haystack Syndrome: Sifting Information out of the Data Ocean</em>. North River Press, 1990.</p></li><li><p>LeanProduction. <em>Theory of Constraints</em>. <a href="https://www.leanproduction.com/theory-of-constraints/">https://www.leanproduction.com/theory-of-constraints/</a></p></li><li><p>Theory of Constraints Institute. <em>Five Focusing Steps, a Process of On-Going Improvement</em>. <a href="https://www.tocinstitute.org/five-focusing-steps.html">https://www.tocinstitute.org/five-focusing-steps.html</a></p></li><li><p>DORA. <em>State of AI-assisted Software Development 2025</em>. <a href="https://dora.dev/dora-report-2025/">https://dora.dev/dora-report-2025/</a></p></li><li><p>Google Cloud. <em>Announcing the 2024 DORA report</em>. <a href="https://cloud.google.com/blog/products/devops-sre/announcing-the-2024-dora-report">https://cloud.google.com/blog/products/devops-sre/announcing-the-2024-dora-report</a></p></li><li><p>Google Cloud. <em>Impact of Generative AI in Software Development</em>. <a href="https://dora.dev/ai/gen-ai-report/">https://dora.dev/ai/gen-ai-report/</a></p></li><li><p>Becker, Joel, Rush, Nate, Barnes, Elizabeth, Rein, David. <em>Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity</em>. <a href="https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/">https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/</a></p></li><li><p>Peng, Sida, Kalliamvakou, Eirini, Cihon, Peter, Demirer, Mert. <em>The Impact of AI on Developer Productivity: Evidence from GitHub Copilot</em>. <a href="https://arxiv.org/abs/2302.06590">https://arxiv.org/abs/2302.06590</a></p></li><li><p>Little, John D. C. <em>A Proof for the Queuing Formula: L = &#955;W</em>. Operations Research, 1961. <a href="https://ideas.repec.org/a/inm/oropre/v9y1961i3p383-387.html">https://ideas.repec.org/a/inm/oropre/v9y1961i3p383-387.html</a></p></li><li><p>IFRS Foundation. <em>IAS 38 Intangible Assets</em>. <a href="https://www.ifrs.org/issued-standards/list-of-standards/ias-38-intangible-assets/">https://www.ifrs.org/issued-standards/list-of-standards/ias-38-intangible-assets/</a></p></li><li><p>Financial Accounting Standards Board. <em>Accounting for and Disclosure of Software Costs</em>. <a href="https://www.fasb.org/page/PageContent?pageId=/projects/recently-completed-projects/accounting-for-and-disclosure-of-software-costs.html">https://www.fasb.org/page/PageContent?pageId=/projects/recently-completed-projects/accounting-for-and-disclosure-of-software-costs.html</a></p></li><li><p>Artiles, Alfredo. <em>Systems Thinking Radar, Vol. 2</em>. </p></li></ul><div class="embedded-post-wrap" data-attrs="{&quot;id&quot;:150946970,&quot;url&quot;:&quot;https://alfredoartiles.substack.com/p/systems-thinking-radar-vol-2&quot;,&quot;publication_id&quot;:2492045,&quot;embedding_publication_id&quot;:null,&quot;publication_name&quot;:&quot;Pensando en Sistemas el Desarrollo de Producto&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!mML5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff58e229-7b20-4e04-8037-09761e6e8a89_1024x1024.png&quot;,&quot;title&quot;:&quot;Systems Thinking Radar - Vol. 2&quot;,&quot;truncated_body_text&quot;:&quot;Systems Thinking Radar es un formato m&#225;s ligero donde poder compartir notas m&#225;s en crudo sobre el contenido que voy consumiendo, a la vez que me permita revisitar algunos conceptos ya tratados, pero en un formato m&#225;s resumido.Thanks for reading Pensando en Sistemas el Desarrollo de Producto! Subscribe for free to receive new posts and support my work.&quot;,&quot;date&quot;:&quot;2024-10-31T08:24:16.187Z&quot;,&quot;like_count&quot;:0,&quot;comment_count&quot;:0,&quot;bylines&quot;:[{&quot;id&quot;:18199212,&quot;name&quot;:&quot;Alfredo Artiles&quot;,&quot;handle&quot;:&quot;aartiles&quot;,&quot;previous_name&quot;:null,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/82e1f511-84e0-4f4d-b7b9-89c990b49045_593x597.jpeg&quot;,&quot;bio&quot;:&quot;Co-founder &amp; CTO at Audiense. Always Cuban. Swimmer and powerlifter wannabe. Like playing chess in my spare time.&quot;,&quot;profile_set_up_at&quot;:&quot;2023-05-20T17:21:56.177Z&quot;,&quot;reader_installed_at&quot;:&quot;2023-05-25T06:16:24.886Z&quot;,&quot;publicationUsers&quot;:[{&quot;id&quot;:2521962,&quot;user_id&quot;:18199212,&quot;publication_id&quot;:2492045,&quot;role&quot;:&quot;admin&quot;,&quot;public&quot;:true,&quot;is_primary&quot;:true,&quot;publication&quot;:{&quot;id&quot;:2492045,&quot;name&quot;:&quot;Pensando en Sistemas el Desarrollo de Producto&quot;,&quot;subdomain&quot;:&quot;alfredoartiles&quot;,&quot;custom_domain&quot;:null,&quot;custom_domain_optional&quot;:false,&quot;hero_text&quot;:&quot;Explorando a trav&#233;s del pensamiento sist&#233;mico las complejidades del desarrollo de producto desde los valores y principios de eXtreme Programming, la mentalidad DevOps y Lean Software/Product Development&quot;,&quot;logo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ff58e229-7b20-4e04-8037-09761e6e8a89_1024x1024.png&quot;,&quot;author_id&quot;:18199212,&quot;primary_user_id&quot;:18199212,&quot;theme_var_background_pop&quot;:&quot;#45D800&quot;,&quot;created_at&quot;:&quot;2024-04-05T14:45:16.070Z&quot;,&quot;email_from_name&quot;:null,&quot;copyright&quot;:&quot;Alfredo Artiles&quot;,&quot;founding_plan_name&quot;:null,&quot;community_enabled&quot;:true,&quot;invite_only&quot;:false,&quot;payments_state&quot;:&quot;disabled&quot;,&quot;language&quot;:&quot;es&quot;,&quot;explicit&quot;:false,&quot;homepage_type&quot;:&quot;newspaper&quot;,&quot;is_personal_mode&quot;:false,&quot;logo_url_wide&quot;:null}}],&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null,&quot;status&quot;:{&quot;bestsellerTier&quot;:null,&quot;subscriberTier&quot;:1,&quot;leaderboard&quot;:null,&quot;vip&quot;:false,&quot;badge&quot;:{&quot;type&quot;:&quot;subscriber&quot;,&quot;tier&quot;:1,&quot;accent_colors&quot;:null},&quot;paidPublicationIds&quot;:[64099],&quot;subscriber&quot;:null}}],&quot;utm_campaign&quot;:null,&quot;belowTheFold&quot;:true,&quot;type&quot;:&quot;newsletter&quot;,&quot;language&quot;:&quot;en&quot;,&quot;source&quot;:null}" data-component-name="EmbeddedPostToDOM"><a class="embedded-post" native="true" href="https://alfredoartiles.substack.com/p/systems-thinking-radar-vol-2?utm_source=substack&amp;utm_campaign=post_embed&amp;utm_medium=web"><div class="embedded-post-header"><img class="embedded-post-publication-logo" src="https://substackcdn.com/image/fetch/$s_!mML5!,w_56,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff58e229-7b20-4e04-8037-09761e6e8a89_1024x1024.png" loading="lazy"><span class="embedded-post-publication-name">Pensando en Sistemas el Desarrollo de Producto</span></div><div class="embedded-post-title-wrapper"><div class="embedded-post-title">Systems Thinking Radar - Vol. 2</div></div><div class="embedded-post-body">Systems Thinking Radar es un formato m&#225;s ligero donde poder compartir notas m&#225;s en crudo sobre el contenido que voy consumiendo, a la vez que me permita revisitar algunos conceptos ya tratados, pero en un formato m&#225;s resumido.Thanks for reading Pensando en Sistemas el Desarrollo de Producto! Subscribe for free to receive new posts and support my work&#8230;</div><div class="embedded-post-cta-wrapper"><span class="embedded-post-cta">Read more</span></div><div class="embedded-post-meta">2 years ago &#183; Alfredo Artiles</div></a></div>]]></content:encoded></item></channel></rss>