<?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[Software Design: Tidy First?: Business architecture]]></title><description><![CDATA[Elements of a business architecture intended to support efficient automated processing in spite of data changes & corrections.]]></description><link>https://newsletter.kentbeck.com/s/business-architecture</link><image><url>https://newsletter.kentbeck.com/img/substack.png</url><title>Software Design: Tidy First?: Business architecture</title><link>https://newsletter.kentbeck.com/s/business-architecture</link></image><generator>Substack</generator><lastBuildDate>Thu, 11 Jun 2026 01:33:11 GMT</lastBuildDate><atom:link href="https://newsletter.kentbeck.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Kent Beck]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[sponsorships@kentbeck.com]]></webMaster><itunes:owner><itunes:email><![CDATA[sponsorships@kentbeck.com]]></itunes:email><itunes:name><![CDATA[Kent Beck]]></itunes:name></itunes:owner><itunes:author><![CDATA[Kent Beck]]></itunes:author><googleplay:owner><![CDATA[sponsorships@kentbeck.com]]></googleplay:owner><googleplay:email><![CDATA[sponsorships@kentbeck.com]]></googleplay:email><googleplay:author><![CDATA[Kent Beck]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Eventual Business Consistency]]></title><description><![CDATA[Executive Summary of Bi-temporality]]></description><link>https://newsletter.kentbeck.com/p/eventual-business-consistency</link><guid isPermaLink="false">https://newsletter.kentbeck.com/p/eventual-business-consistency</guid><dc:creator><![CDATA[Kent Beck]]></dc:creator><pubDate>Fri, 04 Aug 2023 13:49:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/8d3f52a4-8f66-4cb5-a300-0e1d743adaea_669x258.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I&#8217;m a geek speaking to you, a technology-savvy executive, about why we are doing things in a more complicated way than seems necessary. You may have heard the word &#8220;bi-temporal&#8221;. What&#8217;s that about?</p><p>In a nutshell, we want what&#8217;s recorded in the system to match the real world. We know this is impossible (delays, mistakes, changes) but are getting as close as we can. The promise is that if what&#8217;s in the system matches the real world as closely as possible, costs go down, customer satisfaction goes up, &amp; we are able to scale further faster.</p><p>Here&#8217;s how it works.</p><h2>Scenarios</h2><p>We&#8217;ll take addresses as our example. Addresses are useful for sending correspondence, calculating taxes, determining regulations, &amp; targeting marketing. Addresses, though, change.</p><p><strong>Simplest</strong>&#8212;we&#8217;ll just store the address in the database. When the address changes, we&#8217;ll change what&#8217;s in the database. Finito.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!INCU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe43f54dc-3b90-4ca1-90a4-3789943e157a_1500x443.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!INCU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe43f54dc-3b90-4ca1-90a4-3789943e157a_1500x443.png 424w, https://substackcdn.com/image/fetch/$s_!INCU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe43f54dc-3b90-4ca1-90a4-3789943e157a_1500x443.png 848w, https://substackcdn.com/image/fetch/$s_!INCU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe43f54dc-3b90-4ca1-90a4-3789943e157a_1500x443.png 1272w, https://substackcdn.com/image/fetch/$s_!INCU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe43f54dc-3b90-4ca1-90a4-3789943e157a_1500x443.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!INCU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe43f54dc-3b90-4ca1-90a4-3789943e157a_1500x443.png" width="1456" height="430" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e43f54dc-3b90-4ca1-90a4-3789943e157a_1500x443.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:430,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:668086,&quot;alt&quot;:&quot;Change the database when the data changes&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Change the database when the data changes" title="Change the database when the data changes" srcset="https://substackcdn.com/image/fetch/$s_!INCU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe43f54dc-3b90-4ca1-90a4-3789943e157a_1500x443.png 424w, https://substackcdn.com/image/fetch/$s_!INCU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe43f54dc-3b90-4ca1-90a4-3789943e157a_1500x443.png 848w, https://substackcdn.com/image/fetch/$s_!INCU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe43f54dc-3b90-4ca1-90a4-3789943e157a_1500x443.png 1272w, https://substackcdn.com/image/fetch/$s_!INCU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe43f54dc-3b90-4ca1-90a4-3789943e157a_1500x443.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 role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Not quite. The customer calls and says, &#8220;Why did you charge me California sales tax for this order? I don&#8217;t live in California. &#129324;&#8221; We look in the database &amp; there it is, a California address. &#8220;I just moved you numbskulls. The order was sent to me when I was in Colorado.&#8221;</p><p>We&#8217;re terribly sorry for the mistake. Here&#8217;s a voucher for an ice cream cone.</p><p>Not a good experience for the customer. Not a good experience for us.</p><p><strong>Dated data</strong>&#8212;we choose to remember the history of all the addresses &amp; tag them with their date. This is more complicated. Now the customer service screens have to display a list of addresses instead of just one. The database will be bigger because we don&#8217;t throw addresses away. The code will be more complicated because we can no longer say, &#8220;Here&#8217;s a customer. What&#8217;s their address?&#8221; we have to say, &#8220;Here&#8217;s a customer. What&#8217;s their address on this date?&#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_!YNZO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a615322-6e13-4ef0-a86e-af27f9f8bcd1_1482x375.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YNZO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a615322-6e13-4ef0-a86e-af27f9f8bcd1_1482x375.png 424w, https://substackcdn.com/image/fetch/$s_!YNZO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a615322-6e13-4ef0-a86e-af27f9f8bcd1_1482x375.png 848w, https://substackcdn.com/image/fetch/$s_!YNZO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a615322-6e13-4ef0-a86e-af27f9f8bcd1_1482x375.png 1272w, https://substackcdn.com/image/fetch/$s_!YNZO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a615322-6e13-4ef0-a86e-af27f9f8bcd1_1482x375.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YNZO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a615322-6e13-4ef0-a86e-af27f9f8bcd1_1482x375.png" width="1456" height="368" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5a615322-6e13-4ef0-a86e-af27f9f8bcd1_1482x375.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:368,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Add an entry to the database, tagged with the date, when the data changes&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="Add an entry to the database, tagged with the date, when the data changes" title="Add an entry to the database, tagged with the date, when the data changes" srcset="https://substackcdn.com/image/fetch/$s_!YNZO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a615322-6e13-4ef0-a86e-af27f9f8bcd1_1482x375.png 424w, https://substackcdn.com/image/fetch/$s_!YNZO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a615322-6e13-4ef0-a86e-af27f9f8bcd1_1482x375.png 848w, https://substackcdn.com/image/fetch/$s_!YNZO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a615322-6e13-4ef0-a86e-af27f9f8bcd1_1482x375.png 1272w, https://substackcdn.com/image/fetch/$s_!YNZO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a615322-6e13-4ef0-a86e-af27f9f8bcd1_1482x375.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Yes, it&#8217;s more complicated, but in return we get to answer our irate customer&#8217;s question. &#8220;Our records show that you placed the order on June 15 &amp; you moved on June 1.&#8221; Oh.</p><p>Okay, so date-tagged data is better for us at the cost of a bit more complexity. </p><p>However&#8230; What happens when a customer says, &#8220;Oh, by the way, I moved 2 months ago.&#8221;? What date do we use for the tag? Today? Then we won&#8217;t know that we need to recalculate 2 months&#8217; worth of statements. Two months ago? Then we won&#8217;t be able to explain (to the customer, tax authorities, whoever) why we did what we did.</p><p>The fundamental, inescapable problem? What is in the system is a flawed reflection of what is going on in reality. We want what is in the system to be as close as possible to reality, but we also need to acknowledge that consistency between the system &amp; reality will only ever be approached, not achieved. The system will record changes in reality eventually, but by then we may have made decisions that need to be undone.</p><p>Sound difficult? Only a little more than what we&#8217;ve done already.</p><p><strong>Double-dated data</strong>&#8212;we tag each bit of business data with 2 dates:</p><ul><li><p>The date on which the data changed out in the real world, the <em>effective</em> date.</p></li><li><p>The date on which the system found out about the change, the <em>posting</em> date.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VhXw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23bfe21-9587-4d80-8a89-544676e04850_2049x372.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VhXw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23bfe21-9587-4d80-8a89-544676e04850_2049x372.png 424w, https://substackcdn.com/image/fetch/$s_!VhXw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23bfe21-9587-4d80-8a89-544676e04850_2049x372.png 848w, https://substackcdn.com/image/fetch/$s_!VhXw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23bfe21-9587-4d80-8a89-544676e04850_2049x372.png 1272w, https://substackcdn.com/image/fetch/$s_!VhXw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23bfe21-9587-4d80-8a89-544676e04850_2049x372.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VhXw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23bfe21-9587-4d80-8a89-544676e04850_2049x372.png" width="1456" height="264" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a23bfe21-9587-4d80-8a89-544676e04850_2049x372.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:264,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Tag the data with both the date it changed &amp; the date we recorded the change&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="Tag the data with both the date it changed &amp; the date we recorded the change" title="Tag the data with both the date it changed &amp; the date we recorded the change" srcset="https://substackcdn.com/image/fetch/$s_!VhXw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23bfe21-9587-4d80-8a89-544676e04850_2049x372.png 424w, https://substackcdn.com/image/fetch/$s_!VhXw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23bfe21-9587-4d80-8a89-544676e04850_2049x372.png 848w, https://substackcdn.com/image/fetch/$s_!VhXw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23bfe21-9587-4d80-8a89-544676e04850_2049x372.png 1272w, https://substackcdn.com/image/fetch/$s_!VhXw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23bfe21-9587-4d80-8a89-544676e04850_2049x372.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p> This is the simple case where effective &amp; posting dates match.</p><p>(These 2 dates are why we call such systems &#8220;bi-temporal&#8221;. We have 2 timelines. One is the timeline of when things actually happened, the other the timeline of when we found out about it. The purpose of the 2 dates is to make sure that our system is eventually consistent with reality.)</p><p>Another way to look at the example above is to show the timelines explicitly.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IIYE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df299cd-1688-4cad-8bb9-7dc3d96283ef_1157x341.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IIYE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df299cd-1688-4cad-8bb9-7dc3d96283ef_1157x341.png 424w, https://substackcdn.com/image/fetch/$s_!IIYE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df299cd-1688-4cad-8bb9-7dc3d96283ef_1157x341.png 848w, https://substackcdn.com/image/fetch/$s_!IIYE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df299cd-1688-4cad-8bb9-7dc3d96283ef_1157x341.png 1272w, https://substackcdn.com/image/fetch/$s_!IIYE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df299cd-1688-4cad-8bb9-7dc3d96283ef_1157x341.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IIYE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df299cd-1688-4cad-8bb9-7dc3d96283ef_1157x341.png" width="1157" height="341" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5df299cd-1688-4cad-8bb9-7dc3d96283ef_1157x341.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:341,&quot;width&quot;:1157,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Graphical depiction of the same scenario with a timeline for effective date on top &amp; posting date on the bottom &amp; a labelled arrow for the change&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="Graphical depiction of the same scenario with a timeline for effective date on top &amp; posting date on the bottom &amp; a labelled arrow for the change" title="Graphical depiction of the same scenario with a timeline for effective date on top &amp; posting date on the bottom &amp; a labelled arrow for the change" srcset="https://substackcdn.com/image/fetch/$s_!IIYE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df299cd-1688-4cad-8bb9-7dc3d96283ef_1157x341.png 424w, https://substackcdn.com/image/fetch/$s_!IIYE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df299cd-1688-4cad-8bb9-7dc3d96283ef_1157x341.png 848w, https://substackcdn.com/image/fetch/$s_!IIYE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df299cd-1688-4cad-8bb9-7dc3d96283ef_1157x341.png 1272w, https://substackcdn.com/image/fetch/$s_!IIYE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5df299cd-1688-4cad-8bb9-7dc3d96283ef_1157x341.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>And Now&#8230;</h2><p>Using effective &amp; posting dates together we can record all the strange twists &amp; turns of feeding data into a system.</p><p>&#8220;I forgot to tell you that I moved last year.&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fh63!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc567f8e2-a87f-4c14-9862-9f877eb000d9_2015x278.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fh63!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc567f8e2-a87f-4c14-9862-9f877eb000d9_2015x278.png 424w, https://substackcdn.com/image/fetch/$s_!fh63!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc567f8e2-a87f-4c14-9862-9f877eb000d9_2015x278.png 848w, https://substackcdn.com/image/fetch/$s_!fh63!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc567f8e2-a87f-4c14-9862-9f877eb000d9_2015x278.png 1272w, https://substackcdn.com/image/fetch/$s_!fh63!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc567f8e2-a87f-4c14-9862-9f877eb000d9_2015x278.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fh63!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc567f8e2-a87f-4c14-9862-9f877eb000d9_2015x278.png" width="1456" height="201" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c567f8e2-a87f-4c14-9862-9f877eb000d9_2015x278.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:201,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:650427,&quot;alt&quot;:&quot;The arrow slants left for a retroactive change&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The arrow slants left for a retroactive change" title="The arrow slants left for a retroactive change" srcset="https://substackcdn.com/image/fetch/$s_!fh63!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc567f8e2-a87f-4c14-9862-9f877eb000d9_2015x278.png 424w, https://substackcdn.com/image/fetch/$s_!fh63!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc567f8e2-a87f-4c14-9862-9f877eb000d9_2015x278.png 848w, https://substackcdn.com/image/fetch/$s_!fh63!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc567f8e2-a87f-4c14-9862-9f877eb000d9_2015x278.png 1272w, https://substackcdn.com/image/fetch/$s_!fh63!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc567f8e2-a87f-4c14-9862-9f877eb000d9_2015x278.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#8220;You got last year&#8217;s address change wrong.&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ThIn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622d963e-7c24-48be-a733-4007875bfbc6_673x223.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ThIn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622d963e-7c24-48be-a733-4007875bfbc6_673x223.png 424w, https://substackcdn.com/image/fetch/$s_!ThIn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622d963e-7c24-48be-a733-4007875bfbc6_673x223.png 848w, https://substackcdn.com/image/fetch/$s_!ThIn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622d963e-7c24-48be-a733-4007875bfbc6_673x223.png 1272w, https://substackcdn.com/image/fetch/$s_!ThIn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622d963e-7c24-48be-a733-4007875bfbc6_673x223.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ThIn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622d963e-7c24-48be-a733-4007875bfbc6_673x223.png" width="369" height="122.26894502228826" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/622d963e-7c24-48be-a733-4007875bfbc6_673x223.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:223,&quot;width&quot;:673,&quot;resizeWidth&quot;:369,&quot;bytes&quot;:189943,&quot;alt&quot;:&quot;The arrow slants left &amp; lands on the same effective date as the previous change&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The arrow slants left &amp; lands on the same effective date as the previous change" title="The arrow slants left &amp; lands on the same effective date as the previous change" srcset="https://substackcdn.com/image/fetch/$s_!ThIn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622d963e-7c24-48be-a733-4007875bfbc6_673x223.png 424w, https://substackcdn.com/image/fetch/$s_!ThIn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622d963e-7c24-48be-a733-4007875bfbc6_673x223.png 848w, https://substackcdn.com/image/fetch/$s_!ThIn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622d963e-7c24-48be-a733-4007875bfbc6_673x223.png 1272w, https://substackcdn.com/image/fetch/$s_!ThIn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622d963e-7c24-48be-a733-4007875bfbc6_673x223.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#8220;I&#8217;m going to move next year.&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oxEJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e08b3f5-b132-43e3-afe2-f1394d666d1b_663x271.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oxEJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e08b3f5-b132-43e3-afe2-f1394d666d1b_663x271.png 424w, https://substackcdn.com/image/fetch/$s_!oxEJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e08b3f5-b132-43e3-afe2-f1394d666d1b_663x271.png 848w, https://substackcdn.com/image/fetch/$s_!oxEJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e08b3f5-b132-43e3-afe2-f1394d666d1b_663x271.png 1272w, https://substackcdn.com/image/fetch/$s_!oxEJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e08b3f5-b132-43e3-afe2-f1394d666d1b_663x271.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oxEJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e08b3f5-b132-43e3-afe2-f1394d666d1b_663x271.png" width="381" height="155.73303167420815" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e08b3f5-b132-43e3-afe2-f1394d666d1b_663x271.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:271,&quot;width&quot;:663,&quot;resizeWidth&quot;:381,&quot;bytes&quot;:211181,&quot;alt&quot;:&quot;The arrow slants right for a prospective change&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The arrow slants right for a prospective change" title="The arrow slants right for a prospective change" srcset="https://substackcdn.com/image/fetch/$s_!oxEJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e08b3f5-b132-43e3-afe2-f1394d666d1b_663x271.png 424w, https://substackcdn.com/image/fetch/$s_!oxEJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e08b3f5-b132-43e3-afe2-f1394d666d1b_663x271.png 848w, https://substackcdn.com/image/fetch/$s_!oxEJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e08b3f5-b132-43e3-afe2-f1394d666d1b_663x271.png 1272w, https://substackcdn.com/image/fetch/$s_!oxEJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e08b3f5-b132-43e3-afe2-f1394d666d1b_663x271.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>And this is the magic one, the nightmare scenario that just can&#8217;t be automatically handled otherwise.</p><p>&#8220;You got last year&#8217;s address change wrong. I actually moved 2 years ago.&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!g2ah!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff224af63-e6b8-426f-b7c1-9321af870a71_669x258.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!g2ah!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff224af63-e6b8-426f-b7c1-9321af870a71_669x258.png 424w, https://substackcdn.com/image/fetch/$s_!g2ah!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff224af63-e6b8-426f-b7c1-9321af870a71_669x258.png 848w, https://substackcdn.com/image/fetch/$s_!g2ah!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff224af63-e6b8-426f-b7c1-9321af870a71_669x258.png 1272w, https://substackcdn.com/image/fetch/$s_!g2ah!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff224af63-e6b8-426f-b7c1-9321af870a71_669x258.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!g2ah!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff224af63-e6b8-426f-b7c1-9321af870a71_669x258.png" width="407" height="156.9596412556054" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f224af63-e6b8-426f-b7c1-9321af870a71_669x258.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:258,&quot;width&quot;:669,&quot;resizeWidth&quot;:407,&quot;bytes&quot;:208237,&quot;alt&quot;:&quot;One arrow crosses the other for a retroactive correction&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="One arrow crosses the other for a retroactive correction" title="One arrow crosses the other for a retroactive correction" srcset="https://substackcdn.com/image/fetch/$s_!g2ah!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff224af63-e6b8-426f-b7c1-9321af870a71_669x258.png 424w, https://substackcdn.com/image/fetch/$s_!g2ah!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff224af63-e6b8-426f-b7c1-9321af870a71_669x258.png 848w, https://substackcdn.com/image/fetch/$s_!g2ah!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff224af63-e6b8-426f-b7c1-9321af870a71_669x258.png 1272w, https://substackcdn.com/image/fetch/$s_!g2ah!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff224af63-e6b8-426f-b7c1-9321af870a71_669x258.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>To accurately process this scenario we need to <em>undo</em> 2 years&#8217; worth of processing, <em>redo</em> those years with the correct address, the continue from there. Without both dates we&#8217;re throwing the corrections into an account called &#8220;Manual Corrections&#8221; &amp; praying those corrections won&#8217;t cause problems in the future (praying in vain, as it turns out).</p><h2>Conclusion</h2><p>The goal of our design is to provide eventual business consistency, for what&#8217;s recorded in the system to match what&#8217;s happening in the real world as well as possible at a reasonable cost. Since perfect consistency is impossible:</p><ul><li><p>We save everything we learn.</p></li><li><p>We track when changes occurred in reality.</p></li><li><p>We track when we found out about those changes in the system.</p></li></ul><p>We invest additional programming complexity so we can:</p><ul><li><p>Reduce costs (fewer complaints, faster resolution).</p></li><li><p>Reduce mistakes.</p></li><li><p>Improve compliance.</p></li></ul><p>That&#8217;s the tradeoff. That&#8217;s what we&#8217;re doing we say we are bi-temporal. GeekSpeak for &#8220;better business&#8221;.</p><blockquote><p>I&#8217;d like to thank Massimo Arnoldi &amp; the <a href="https://www.lifeware.ch/">Lifeware</a> gang for teaching me bi-temporality, teaching me how to effectively visualize the two timelines, &amp; using bi-temporality to provide outstanding customer service to millions of insurance customers for decades. </p></blockquote><h2>Appendix: The Analogy</h2><p>Bi-temporal data has been around since the early 1990&#8217;s, based on the pioneering work of <a href="https://en.wikipedia.org/wiki/Richard_T._Snodgrass">Richard Snodgrass</a>. Part of the reason it hasn&#8217;t taken off is because of the additional complexity it imposes on programmers. However, I think part of the reason it hasn&#8217;t become more popular, given the benefits it brings, is just the name. Hence my proposed rebranding to &#8220;eventual business consistency&#8221;.</p><p>Unfortunately, that name is itself a geeky analogy. If you already understand &#8220;eventual consistency&#8221;, the analogy makes immediate sense. If not, probably not so much. At the risk of &#8220;explaining the joke&#8221; (which never works, right?), here is the analogy.</p><p>Say we have a critically important database. We store the data on 2 machines so if one machine crashes we still have access to our data.</p><p>Say the network between the 2 machines is flaky (protip: it is). When we write data to one machine &amp; the network is down we can either:</p><ul><li><p>Wait for the network to come back, which imposes unpredictable delays. If the data absolutely, positively have to be consistent between the 2 machines we may be willing to pay this cost.</p></li><li><p>Write the data to one machine now &amp; catch up later. If we absolutely, positively need to be able to write data at all times but it&#8217;s okay if the data is a little out of sync, this is an acceptable tradeoff. We call this scenario &#8220;eventual consistency&#8221;.</p></li></ul><p>It&#8217;s this latter, &#8220;catch up later&#8221;, strategy that we draw our analogy from. Just as the 2 databases are consistent, but only eventually, our system data matches reality, but only eventually. We acknowledge that the system &amp; reality are a little out of whack but we&#8217;re transparent about inconsistencies &amp; fix them as well as possible as soon as possible.</p>]]></content:encoded></item><item><title><![CDATA[Accounts & Transactions]]></title><description><![CDATA[In this series we explore a business architecture that&#8217;s more complex than obviously necessary, but where each complication creates value over time.]]></description><link>https://newsletter.kentbeck.com/p/accounts-and-transactions</link><guid isPermaLink="false">https://newsletter.kentbeck.com/p/accounts-and-transactions</guid><dc:creator><![CDATA[Kent Beck]]></dc:creator><pubDate>Wed, 12 Jul 2023 14:38:04 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!NAR7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6652527d-bc71-4259-87ba-d76763b845b7_2960x1323.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a href="https://tidyfirst.substack.com/p/what-if-you-are-going-to-need-it">In</a> <a href="https://tidyfirst.substack.com/p/never-forget">this</a> <a href="https://tidyfirst.substack.com/p/when-did-it-happen-when-did-we-find">series</a> we explore a business architecture that&#8217;s more complex than obviously necessary, but where each complication creates value over time.</p><blockquote><p>Watch designers speak in terms of &#8220;complications&#8221; instead of &#8220;features&#8221;. I love this framing because it simultaneously reminds everyone of both costs &amp; benefits.</p></blockquote><p>So far we have discussed:</p><ol><li><p>We want the flexibility of human processing with the scale &amp; economy of automated processing.</p></li><li><p>So we save all data needed to recreate the history of a contract.</p></li><li><p>But the real world out there &amp; the ideal world of the system diverge.</p></li><li><p>So we tag all data with both the date on which data became true in the real world &amp; the date on which the system learned about that data (bi-temporality).</p></li></ol><p>Now we come to the next complication: reproduction. We would like to be able to re-run the entire history of a contract from inception to the present moment &amp; get exactly the same results as those we currently have stored (the actual differences will be interesting). </p><p>&#8220;That fee is outrageous! I&#8217;ll only pay half that.&#8221; &#8220;But that was 5 years ago!&#8221; &#8220;I don&#8217;t care.&#8221; Imagine being able to retroactively change the fee, re-run the entire 5 years worth of transactions, &amp; confidently seeing how the balances have changed.</p><p>To replay history we must record it.</p><h2>Accounts &amp; Transactions</h2><p>Therefore, record monetary flows as transactions between accounts. Bi-temporally stamp these transactions. Implement business logic by reading account balances &amp; posting new transactions.</p><blockquote><p>There is a whole Tao Te Ching &#36947;&#24503;&#32147; interpretation of double entry bookkeeping which I&#8217;m not going to describe here because I&#8217;m no initiate. Feel free to explore.</p></blockquote><p>What accounts do you need? That&#8217;s a matter of style, judgement, &amp; iteration. In general, have an account any time a balance is interesting to someone. &#8220;How much have I paid in premiums?&#8221; Account. &#8220;How much risk did we re-insure?&#8221; Account. &#8220;What is the current street address?&#8221; Not an account (but maybe it should be&#8212;try it some time).</p><h2>Implementation: Structure</h2><blockquote><p>SubStack is responding differently to indenting than it has before for me. I&#8217;m afraid you&#8217;ll have to fill in the leading whitespace yourself until I get it figured out.</p></blockquote><p>Here is the basic test we&#8217;d like to pass:</p><p><code>monday = 1<br>tuesday = 2<br>wednesday = 3<br>thursday = 4<br>source = Account()<br>destination = Account()<br>Transaction.post(source, destination, 100, tuesdayAsOfTuesday)<br>mondayAsOfMonday = Perspective(effective=monday, posting=monday)<br>assert source.balance(mondayAsOfMonday) == 0<br>tuesdayAsOfTuesday = Perspective(tuesday, tuesday)<br>assert source.balance(tuesdayAsOfTuesday) == -100<br>tuesdayAsOfThursday = Perspective(thursday, tuesday)<br>assert destination.balance(tuesdayAsOfThursday) == 100</code></p><p>Let&#8217;s take this top down. The object structure we want to create 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_!NAR7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6652527d-bc71-4259-87ba-d76763b845b7_2960x1323.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NAR7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6652527d-bc71-4259-87ba-d76763b845b7_2960x1323.png 424w, https://substackcdn.com/image/fetch/$s_!NAR7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6652527d-bc71-4259-87ba-d76763b845b7_2960x1323.png 848w, https://substackcdn.com/image/fetch/$s_!NAR7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6652527d-bc71-4259-87ba-d76763b845b7_2960x1323.png 1272w, https://substackcdn.com/image/fetch/$s_!NAR7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6652527d-bc71-4259-87ba-d76763b845b7_2960x1323.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NAR7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6652527d-bc71-4259-87ba-d76763b845b7_2960x1323.png" width="1456" height="651" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6652527d-bc71-4259-87ba-d76763b845b7_2960x1323.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:651,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4047895,&quot;alt&quot;:&quot;The source account's debits contains the transaction &amp; the destination accounts credits contains the transaction.&quot;,&quot;title&quot;:&quot;The source account's debits contains the transaction &amp; the destination accounts credits contains the transaction.&quot;,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The source account's debits contains the transaction &amp; the destination accounts credits contains the transaction." title="The source account's debits contains the transaction &amp; the destination accounts credits contains the transaction." srcset="https://substackcdn.com/image/fetch/$s_!NAR7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6652527d-bc71-4259-87ba-d76763b845b7_2960x1323.png 424w, https://substackcdn.com/image/fetch/$s_!NAR7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6652527d-bc71-4259-87ba-d76763b845b7_2960x1323.png 848w, https://substackcdn.com/image/fetch/$s_!NAR7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6652527d-bc71-4259-87ba-d76763b845b7_2960x1323.png 1272w, https://substackcdn.com/image/fetch/$s_!NAR7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6652527d-bc71-4259-87ba-d76763b845b7_2960x1323.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The external API for posting a transaction seems to fit nicely as a factory method on Transaction:</p><p><code>class Transaction:<br>  @staticmethod<br>  def post(source, destination, amount, perspective):<br>    result = Transaction(source, destination, amount, perspective)<br>    result._post()<br>    return result</code></p><p><code>@staticmethod<br> def post(source, destination, amount, perspective):<br>    result = Transaction(source, destination, amount, perspective)<br>    result._post()<br>    return result</code></p><p>This method both creates side effects &amp; returns a new object. Some folks would tsk tsk &#129292;&#127995;. Too bad. Write your own code.</p><p>Supporting the factory method are the private __init__ &amp; _post methods:</p><p>  <code>  def __init__(self, source, destination, amount, perspective):<br>       self._source = source<br>       self._destination = destination<br>       self._amount = amount<br>       self._perspective = perspective<br>   def perspective(self):<br>       return self._perspective<br>   def _post(self):<br>       self._source.debit(self)<br>       self._destination.credit(self)</code></p><p>An Account stores 2 Histories of Transactions, one for credits &amp; one for debits. (Real accountants are likely cringing by now&#8212;we are using &#8220;accounts &amp; transactions&#8221; as a metaphor, not implementing genuine double entry bookkeeping.)</p><p><code>class Account:<br>    def __init__(self):<br>        self._credits = History()<br>        self._debits = History()<br>    def credit(self, transaction):<br>        self._credits.add(transaction, transaction.perspective())<br>    def debit(self, transaction):<br>        self._debits.add(transaction, transaction.perspective())</code></p><p>(I went back and forth whether to pull the perspective out here or pass it as an explicit parameter. I decided that this better expressed the invariant that the transaction is always stored under its own perspective.)</p><h2>Implementation: Behavior</h2><p>Now that we have the object structure we want, we can implement the balance() method.</p><p><code>class Account:<br>    def balance(self, perspective):<br>        credit = self._balanceOn(self._credits, perspective)<br>        debit = self._balanceOn(self._debits, perspective)<br>        return credit - debit</code></p><p>This relies on a helper that sums the amounts of the visible transactions in a History.</p><p><code>    def _balanceOn(self, transactions, perspective):<br>        return sum(map(lambda x: x.amount(), transactions.seen(perspective)))</code></p><p>If you remember History from the last couple of posts, we need an accessor that returns only the seen elements. Here it is for completeness:</p><p><code>class History:<br>    def _seen(self, perspective):<br>        return filter(lambda x: perspective.sees(x[0]), self._events)<br>    def seen(self, perspective):<br>        return map(lambda x: x[1], self._seen(perspective))</code></p><p>(We are inching ever closer to the dreaded range query for History, which I&#8217;m really hoping to avoid.)</p><h2>Conclusion</h2><p>There you have it. It would seem easy enough to implement some business logic by just querying a bunch of domain objects, writing the results somewhere temporary, summing the results, issuing a report, and then forgetting the intermediate state. This, however, would prevent you from reproducing the current state.</p><p>Instead, consider adding the additional complication of storing intermediate business results as transactions in accounts. Implement business logic by querying balances or unprocessed transactions &amp; posting new transactions. Eventually there will be an account like &#8220;owed to customer&#8221; or &#8220;owed to government&#8221;. Processing those accounts will cause change in the outside world.</p><p>One limitation of this approach is that you can&#8217;t (yet?) easily reproduce earlier states of the code. If you process yesterday&#8217;s data with today&#8217;s logic, you could easily come up with different answers than you did yesterday. In practice this doesn&#8217;t come up that often, but it&#8217;s interesting to imagine automatic solutions. A bi-temporal code repository? Hmmm&#8230;</p><p>The ability to replay history may be surprising &amp; novel to the business. Business invented the concept of &#8220;closing&#8221; (as in &#8220;year-end closing&#8221;) to sweep the sins of the past under the rug. Retroactive changes become exercises in accounting legerdemain. Don&#8217;t expect the business to adapt instantly.</p><p>Note that our transactions aren&#8217;t the only thing we will need to explain the history of a contract. We also need a Document Store (to be described later) to account for all correspondence sent &amp; received. </p>]]></content:encoded></item><item><title><![CDATA[When Did It Happen? When Did We Find Out?]]></title><description><![CDATA[Or, bi-temporality made slightly easier to understand]]></description><link>https://newsletter.kentbeck.com/p/when-did-it-happen-when-did-we-find</link><guid isPermaLink="false">https://newsletter.kentbeck.com/p/when-did-it-happen-when-did-we-find</guid><dc:creator><![CDATA[Kent Beck]]></dc:creator><pubDate>Thu, 22 Jun 2023 18:11:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!dQJL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb11faeb-b6d5-4e09-84a2-d224005d0e88_2268x1106.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p>First, a big shout-out to Massimo Arnoldi &amp; the folks at Lifeware for teaching me these patterns. They were distilled from decades of experience operating tracking let&#8217;s just say a lot of money.</p></blockquote><p>&#8220;When is she moving? When did you heard about this?&#8221; We&#8217;ve all heard this line of dialog on TV. Turns out it&#8217;s important to business system development. Here&#8217;s why.</p><p>In our <a href="https://tidyfirst.substack.com/p/what-if-you-are-going-to-need-it">previous</a> <a href="https://tidyfirst.substack.com/p/never-forget">posts</a>, we developed the themes of this series:</p><ul><li><p>We can create value by moving some design decisions earlier, in spite of the loss of net present value.</p></li><li><p>Business architecture is one of these cases.</p></li><li><p>Combining human judgement &amp; automation gives us flexibility, scalability, &amp; efficiency.</p></li><li><p>We have to reconcile 2 timelines&#8212;what&#8217;s going on in the real world out there &amp; what our simplified system model knows about what&#8217;s going on, leading us to:</p></li><li><p>Elephant Principle&#8212;remember all business information forever.</p></li><li><p>Store data tagged by the time we stored it in the system. This lets us answer questions like, &#8220;What was the coverage last Tuesday?&#8221; (&#8220;Point queries&#8221;)</p></li></ul><h2>Whoopsies Happen</h2><p>So far, so good. The previous model of a Customer let us get and set addresses as of dates.</p><p><code>Customer&#187;setAddress(address, posting)<br>Customer&#187;getAddress(posting)</code></p><p>Now our dual timelines come to bite us. Here&#8217;s a scenario:</p><ol><li><p>On Monday we find out that the address is 123 Main.</p></li><li><p>On Wednesday we find out that the address is now 456 Main. (This customer moves a lot but not far.)</p></li><li><p>Whoopsie! On Thursday we find out that on Tuesday the customer moved to 789 Main.</p></li></ol><p>We could just say:</p><p><code>customer.address(&#8220;789 Main&#8221;, tuesday)</code></p><p>Sort of splice the correction into the history. However, we have all of Wednesday&#8217;s processing out there based on the wrong address. How can we ask, &#8220;What address for Tuesday did I use on Wednesday?&#8221;</p><p>In other words, we want to record both:</p><ul><li><p>When this piece of information entered the system.</p></li><li><p>When this piece of information is changed in the outside world.</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_!dQJL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb11faeb-b6d5-4e09-84a2-d224005d0e88_2268x1106.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dQJL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb11faeb-b6d5-4e09-84a2-d224005d0e88_2268x1106.png 424w, https://substackcdn.com/image/fetch/$s_!dQJL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb11faeb-b6d5-4e09-84a2-d224005d0e88_2268x1106.png 848w, https://substackcdn.com/image/fetch/$s_!dQJL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb11faeb-b6d5-4e09-84a2-d224005d0e88_2268x1106.png 1272w, https://substackcdn.com/image/fetch/$s_!dQJL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb11faeb-b6d5-4e09-84a2-d224005d0e88_2268x1106.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dQJL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb11faeb-b6d5-4e09-84a2-d224005d0e88_2268x1106.png" width="502" height="244.79395604395606" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bb11faeb-b6d5-4e09-84a2-d224005d0e88_2268x1106.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:710,&quot;width&quot;:1456,&quot;resizeWidth&quot;:502,&quot;bytes&quot;:3115118,&quot;alt&quot;:&quot;The address changed Tuesday but we didn't find out about it until Thursday&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The address changed Tuesday but we didn't find out about it until Thursday" title="The address changed Tuesday but we didn't find out about it until Thursday" srcset="https://substackcdn.com/image/fetch/$s_!dQJL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb11faeb-b6d5-4e09-84a2-d224005d0e88_2268x1106.png 424w, https://substackcdn.com/image/fetch/$s_!dQJL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb11faeb-b6d5-4e09-84a2-d224005d0e88_2268x1106.png 848w, https://substackcdn.com/image/fetch/$s_!dQJL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb11faeb-b6d5-4e09-84a2-d224005d0e88_2268x1106.png 1272w, https://substackcdn.com/image/fetch/$s_!dQJL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb11faeb-b6d5-4e09-84a2-d224005d0e88_2268x1106.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Our current model conflates the two.</p><h2>Enter Perspective</h2><p>We need to expand the &#8220;tags&#8221; for business information. Since this tag is a way to look at the system, we&#8217;ll call it &#8220;Perspective&#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_!MvCv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa597cffb-e7ec-4808-bca5-38bf6827f11e_2462x1318.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MvCv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa597cffb-e7ec-4808-bca5-38bf6827f11e_2462x1318.png 424w, https://substackcdn.com/image/fetch/$s_!MvCv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa597cffb-e7ec-4808-bca5-38bf6827f11e_2462x1318.png 848w, https://substackcdn.com/image/fetch/$s_!MvCv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa597cffb-e7ec-4808-bca5-38bf6827f11e_2462x1318.png 1272w, https://substackcdn.com/image/fetch/$s_!MvCv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa597cffb-e7ec-4808-bca5-38bf6827f11e_2462x1318.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MvCv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa597cffb-e7ec-4808-bca5-38bf6827f11e_2462x1318.png" width="478" height="255.74313186813185" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a597cffb-e7ec-4808-bca5-38bf6827f11e_2462x1318.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:779,&quot;width&quot;:1456,&quot;resizeWidth&quot;:478,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Effective date records when something changed in the world. Posting date records when the system found out.&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="Effective date records when something changed in the world. Posting date records when the system found out." title="Effective date records when something changed in the world. Posting date records when the system found out." srcset="https://substackcdn.com/image/fetch/$s_!MvCv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa597cffb-e7ec-4808-bca5-38bf6827f11e_2462x1318.png 424w, https://substackcdn.com/image/fetch/$s_!MvCv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa597cffb-e7ec-4808-bca5-38bf6827f11e_2462x1318.png 848w, https://substackcdn.com/image/fetch/$s_!MvCv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa597cffb-e7ec-4808-bca5-38bf6827f11e_2462x1318.png 1272w, https://substackcdn.com/image/fetch/$s_!MvCv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa597cffb-e7ec-4808-bca5-38bf6827f11e_2462x1318.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><code>class Perspective:<br>&nbsp; &nbsp; def __init__(self, effective, posting):<br>&nbsp; &nbsp; &nbsp; &nbsp; self.effective = effective<br>&nbsp; &nbsp; &nbsp; &nbsp; self.posting = posting<br>&nbsp; &nbsp; def sees(self, other):<br>&nbsp; &nbsp; &nbsp; &nbsp; return other.posting &lt;= self.posting and other.effective &lt;= self.effective</code></p><p>The key piece of behavior here is that one Perspective only &#8220;sees&#8221; another if <em>both</em> the posting &amp; effective times (not really just times, but we&#8217;ll get to that later) are earlier.</p><p>We use Perspective&#187;sees() when we query a History. To find the event corresponding to a Perspective, we </p><ol><li><p>Filter out all the events that can&#8217;t be seen.</p></li><li><p>Sort the visible events by effective date.</p></li><li><p>Return the most recent.</p></li></ol><p><code>class History:<br>&nbsp; &nbsp; def get(self, perspective):<br>&nbsp; &nbsp; &nbsp; &nbsp; visible = filter(lambda x: perspective.sees(x[0]), self._events)<br>&nbsp; &nbsp; &nbsp; &nbsp; freshest = sorted(visible, key=lambda x: x[0].effective, reverse=True)<br>&nbsp; &nbsp; &nbsp; &nbsp; return freshest[0][1]</code></p><p>(Recall that events are stored as a tuple of Perspective (was justing posting) &amp; value. All those [0]&#8217;s are starting to bug me so I&#8217;ll probably fix that soon.)</p><h2>Example</h2><p>Here is the code I used to drive the above implementation.</p><p><code>monday = 1<br>tuesday = 2<br>wednesday = 3<br>thursday = 4<br><br>customer = Customer()<br>mondayAsOfMonday = Perspective(effective=monday, posting=monday)<br>customer.set_address("123", mondayAsOfMonday)<br>tuesdayAsOfThursday = Perspective(thursday, tuesday)<br>customer.set_address("456", tuesdayAsOfThursday)<br><br>assert customer.get_address(mondayAsOfMonday) == "123"<br>assert customer.get_address(tuesdayAsOfThursday) == "456"<br><br>tuesdayAsOfWednesday = Perspective(wednesday, tuesday)<br>assert customer.get_address(tuesdayAsOfWednesday) == "123"</code></p><h2>So What?</h2><p>You might reasonably ask, &#8220;So what? These situations are rare.&#8221; Yes, but&#8230; First, these situations aren&#8217;t all that rare. Run a contract for 20 years &amp; things are bound to get wobbly. Second, the cost of not tracking effective &amp; posting separately compounds. Do you just keep running the contract even though it is wrong? Do you hire a room full of expensive actuaries to manually calculate everything? (I&#8217;ve seen both strategies used.) </p><p>The expected cost of effective/posting mismatches is high. Tracking them both, figuring out the right effective/posting combination, can be tough. It&#8217;s what we need to do to keep processing inexpensive &amp; scalable.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Never Forget]]></title><description><![CDATA[The fun thing about writing this series is that it&#8217;s all stuff I&#8217;ve &#8220;known&#8221; for 25 years.]]></description><link>https://newsletter.kentbeck.com/p/never-forget</link><guid isPermaLink="false">https://newsletter.kentbeck.com/p/never-forget</guid><dc:creator><![CDATA[Kent Beck]]></dc:creator><pubDate>Fri, 16 Jun 2023 14:10:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!jzcJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa604c0b7-7a0d-4ee9-b27f-f6e9d5bc8c9b_1250x1373.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The fun thing about writing this series is that it&#8217;s all stuff I&#8217;ve &#8220;known&#8221; for 25 years. Now, having to explain it, I get to understand <em>why</em>. Understanding why is my biggest driver &amp; here&#8217;s a big chance.</p><p>I took a step too far when I introduced bi-temporality in <a href="https://tidyfirst.substack.com/p/what-if-you-are-going-to-need-it">the first post of this series</a>. I&#8217;m going back to the beginning &amp; start over &amp; take one step forward.</p><h2>The Big Contradiction</h2><p>We want:</p><ul><li><p>The flexibility &amp; reasoning of personal, human attention to the business problems we solve.</p></li><li><p>But also the scalability, repeatability, &amp; efficiency of automated processing.</p></li></ul><p>If we only wanted the first goal, we could just have humans dealing directly with reality. They&#8217;d see someone disabled (&#8216;ll be using disability insurance here as an example) &amp; they would just pay them directly.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jzcJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa604c0b7-7a0d-4ee9-b27f-f6e9d5bc8c9b_1250x1373.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jzcJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa604c0b7-7a0d-4ee9-b27f-f6e9d5bc8c9b_1250x1373.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jzcJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa604c0b7-7a0d-4ee9-b27f-f6e9d5bc8c9b_1250x1373.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jzcJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa604c0b7-7a0d-4ee9-b27f-f6e9d5bc8c9b_1250x1373.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jzcJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa604c0b7-7a0d-4ee9-b27f-f6e9d5bc8c9b_1250x1373.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jzcJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa604c0b7-7a0d-4ee9-b27f-f6e9d5bc8c9b_1250x1373.jpeg" width="352" height="386.6368" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a604c0b7-7a0d-4ee9-b27f-f6e9d5bc8c9b_1250x1373.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1373,&quot;width&quot;:1250,&quot;resizeWidth&quot;:352,&quot;bytes&quot;:298382,&quot;alt&quot;:&quot;Cloudy reality &amp; humans interacting with it directly&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Cloudy reality &amp; humans interacting with it directly" title="Cloudy reality &amp; humans interacting with it directly" srcset="https://substackcdn.com/image/fetch/$s_!jzcJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa604c0b7-7a0d-4ee9-b27f-f6e9d5bc8c9b_1250x1373.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jzcJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa604c0b7-7a0d-4ee9-b27f-f6e9d5bc8c9b_1250x1373.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jzcJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa604c0b7-7a0d-4ee9-b27f-f6e9d5bc8c9b_1250x1373.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jzcJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa604c0b7-7a0d-4ee9-b27f-f6e9d5bc8c9b_1250x1373.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It&#8217;s impossible to make this consistent or scalable. We want to distribute the benefits of disability insurance as widely as possible.</p><h2>The System</h2><p>And so we write The System to model disability contracts. Something is lost, sure, but something is gained (we can debate capitalism &amp; shareholder value another time&#8212;right now we&#8217;re trying to build a system that meets its goals through being more complex than the bare minimum).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ARAO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb53e5af-f4e7-4134-8f9a-d6c4fe24d2a1.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ARAO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb53e5af-f4e7-4134-8f9a-d6c4fe24d2a1.heic 424w, https://substackcdn.com/image/fetch/$s_!ARAO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb53e5af-f4e7-4134-8f9a-d6c4fe24d2a1.heic 848w, https://substackcdn.com/image/fetch/$s_!ARAO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb53e5af-f4e7-4134-8f9a-d6c4fe24d2a1.heic 1272w, https://substackcdn.com/image/fetch/$s_!ARAO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb53e5af-f4e7-4134-8f9a-d6c4fe24d2a1.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ARAO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb53e5af-f4e7-4134-8f9a-d6c4fe24d2a1.heic" width="416" height="333.42857142857144" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bb53e5af-f4e7-4134-8f9a-d6c4fe24d2a1.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1167,&quot;width&quot;:1456,&quot;resizeWidth&quot;:416,&quot;bytes&quot;:420281,&quot;alt&quot;:&quot;Messy reality &amp; the tidy system&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Messy reality &amp; the tidy system" title="Messy reality &amp; the tidy system" srcset="https://substackcdn.com/image/fetch/$s_!ARAO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb53e5af-f4e7-4134-8f9a-d6c4fe24d2a1.heic 424w, https://substackcdn.com/image/fetch/$s_!ARAO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb53e5af-f4e7-4134-8f9a-d6c4fe24d2a1.heic 848w, https://substackcdn.com/image/fetch/$s_!ARAO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb53e5af-f4e7-4134-8f9a-d6c4fe24d2a1.heic 1272w, https://substackcdn.com/image/fetch/$s_!ARAO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb53e5af-f4e7-4134-8f9a-d6c4fe24d2a1.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The people can interact with the system or the system can act scaleably &amp; efficiently.</p><h2>The Simplest Thing That Won&#8217;t Work&#8230;</h2><p>The simplest way to represent reality in the system is to record what we believe to be true at the moment. As a simplified example, suppose we have a Customer with an Address.</p><p><code>customer = Customer()<br>assert customer.address == ""<br>customer.address = "123 Main St."<br>assert customer.address == "123 Main St."</code></p><p>The implementation of this can be dirt simple (ignoring all the juicy stuff because addresses aren&#8217;t strings, use properties, yadda yadda yadda, not my point).</p><p><code>class Customer:<br>    def __init__(self):<br>        self.address = ""</code></p><h2>Two Watches</h2><p>When we split the world into reality out there &amp; what we have recorded in the system, though, we have the Two Watches Problem. When you have one watch, you know what time it is. When you have two watches, you no longer know what time it is because the watches are never exactly the same &amp; you don&#8217;t know which one is right.</p><p>We have two timelines to reconcile. Reality advances one second every second. Our system advances in fits &amp; spurts. Sometimes we jump forward a day. Or a year. Sometimes we learn that something changed in the past &amp; we have to repair changes we made in the meantime.</p><p>How can we handle the disconnect between reality &amp; the system?</p><h2>Save All The Data</h2><p>And so we come to the first principle of business architecture&#8212;never discard information.</p><p>We are in a tradeoff space here&#8212;what is the cost of keeping information versus what is the cost of discarding information? If we discard information we save a little space, but space is cheap. We also simplify our model&#8212;there&#8217;s an address, that&#8217;s the address, end of story. However, when we discard information we are stuck when trying to explain the past. Where did you send last year&#8217;s statement? I don&#8217;t know, we have your correct address now.</p><p>If we save all historical information, we have to pay for that storage both in electrons somewhere but also in the complexity of our model. What&#8217;s the address? The address when?</p><p>For the long term economical functioning of the system, we choose to store all information that enters the system tagged by the moment it entered.</p><h2>So Instead</h2><p>Our interface is going to look like this.</p><p><code>customer = Customer()<br>customer.set_address("123 Main", 1)<br>customer.set_address("456 Main", 2)<br>assert customer.get_address(1) == "123 Main"<br>assert customer.get_address(2) == "456 Main"<br>assert customer.get_address(3) == "456 Main"</code></p><p>I moved down the street as of time 2. Instead of just setting my address, I have to also include a moment as of which the system is going record the address. Instead of just fetching my address, I have to also specify the moment as of which I want my address (we&#8217;ll talk later about the options for the type &amp; granularity of these &#8220;moments&#8221;).</p><p>We replace the single scalar address with a timestamped collection of addresses.</p><p><code>class Customer:<br>    def __init__(self):<br>        self._addresses = History()<br>    def get_address(self, posting):<br>        return self._addresses.get(posting)<br>    def set_address(self, address, posting):<br>        self._addresses.set(address, posting)</code></p><p>As per usual, we cordon off the complexity of storing timestamped values in its own object, History. Setting a value is simple&#8212;just record a (moment, event) tuple. (Yes, yes, this is kind of ugly. We&#8217;ll make it better later. Chill.)</p><p><code>class History:<br>    def __init__(self):<br>        self._events = []<br>    def set(self, address, posting):<br>        self._events.append((posting, address))</code></p><p>The only cleverness to History is fetching. We sort the events in <em>reverse</em> order of posting time, then return the first event whose posting time is <em>before</em> (or the same as) the event&#8217;s posting time.</p><p><code>def get(self, posting):<br>    backwards = sorted(self._events, key=lambda x: x[0], reverse=True)<br>    for eachPosting, eachEvent in backwards:<br>        if eachPosting &lt;= posting:<br>            return eachEvent<br>    raise KeyError()</code></p><h2>Conclusion</h2><p>There you have it, our first &#8220;non-obvious complexity but I promise it&#8217;s going to pay off&#8221; decision. Save all business information forever. Tag the information with the moment we discovered it to be true.</p><p>We&#8217;re not done yet. I said jumped too quickly to bi-temporality. With the code &amp; concepts above we&#8217;re ready to take a short step to motivating the second dimension of time&#8212;effective dating. But that&#8217;s for the next post.</p>]]></content:encoded></item><item><title><![CDATA[What If You *Are* Going To Need It?]]></title><description><![CDATA[Business architecture for efficient, effective operation]]></description><link>https://newsletter.kentbeck.com/p/what-if-you-are-going-to-need-it</link><guid isPermaLink="false">https://newsletter.kentbeck.com/p/what-if-you-are-going-to-need-it</guid><dc:creator><![CDATA[Kent Beck]]></dc:creator><pubDate>Thu, 08 Jun 2023 11:07:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!SCek!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb15fc2fe-6b8f-4a8f-8590-c72fedd34b73_2600x1385.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Back on the C3 project, Chet Hendrickson walked up to me with a design problem. &#8220;We&#8217;re going to need this so the design is going to be complicated. Oh and we&#8217;re going to need that, which complicates things further. And we&#8217;re going to need this other&#8230;&#8221;</p><p>&#8220;You aren&#8217;t going to need it.&#8221;</p><p>&#8220;But we are, and so&#8230;&#8221;</p><p>&#8220;You aren&#8217;t going to need it. What is the simplest thing that could possibly work?&#8221;</p><p>Pause. Thinking. &#8220;I could do this for now.&#8221;</p><p>&#8220;Okay.&#8221;</p><p>Away he went.</p><h2>Curse of Knowledge</h2><p>Chet&#8217;s problem wasn&#8217;t that he didn&#8217;t know enough. His problem was that he thought he had to solve an overwhelming number of problems at once. Those two mantras&#8212;&#8221;You aren&#8217;t going to need it&#8221; &amp; &#8220;What is the simplest thing that could possibly work?&#8221;&#8212;were intended as reminders that it&#8217;s okay to do an okay job today &amp; make it better tomorrow. In fact, because of the time value of money &amp; the likelihood of learning, choosing to defer a decision can create value.</p><h2>Lifeware</h2><p>I had the good fortune last week of visiting Lifeware, a Swiss-based insurance software company I have worked with for 25 years. I noticed that Lifeware solves certain problems in a more complicated way than is obviously necessary. A quarter century of experience has shown, however, that this &#8220;over&#8221; engineering is actual the simplest, most pragmatic solution to problems that most business systems choose not to solve.</p><p>The next few posts introduce you to the Lifeware business architecture. Each post highlights an element of the architecture, its goals, &amp; its consequences.</p><h2>Overall Goal</h2><p>Lifeware manages insurance contracts. It charges its customers per contract per year, so Lifeware&#8217;s sustainability depends on managing these contracts with little human intervention. However, insurance is an incredibly complex domain &amp; programming to manage all possible scenarios is impossible.</p><p>The overall goal of Lifeware&#8217;s business architecture is mostly-automated processing of contracts with computer support when humans need to intervene. After human intervention, automated processing should resume.</p><p>Lifeware &amp; its customers are served by achieving this goal. Policy holders &amp; beneficiaries also benefit. Insurance is full of opaque pricing &amp; fees. Explainable, transparent processing allows end customers to understand what they are paying for &amp; what they are getting.</p><h2>Two Timelines</h2><p>As if insurance as a domain isn&#8217;t complex enough, that complexity is multiplied by operational complexity. There are 2 timelines at work&#8212;the real world &amp; the system. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SCek!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb15fc2fe-6b8f-4a8f-8590-c72fedd34b73_2600x1385.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SCek!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb15fc2fe-6b8f-4a8f-8590-c72fedd34b73_2600x1385.jpeg 424w, https://substackcdn.com/image/fetch/$s_!SCek!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb15fc2fe-6b8f-4a8f-8590-c72fedd34b73_2600x1385.jpeg 848w, https://substackcdn.com/image/fetch/$s_!SCek!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb15fc2fe-6b8f-4a8f-8590-c72fedd34b73_2600x1385.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!SCek!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb15fc2fe-6b8f-4a8f-8590-c72fedd34b73_2600x1385.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SCek!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb15fc2fe-6b8f-4a8f-8590-c72fedd34b73_2600x1385.jpeg" width="1456" height="776" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b15fc2fe-6b8f-4a8f-8590-c72fedd34b73_2600x1385.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:776,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:655901,&quot;alt&quot;:&quot;Two address changes in the external world are immediately reflected in the system&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Two address changes in the external world are immediately reflected in the system" title="Two address changes in the external world are immediately reflected in the system" srcset="https://substackcdn.com/image/fetch/$s_!SCek!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb15fc2fe-6b8f-4a8f-8590-c72fedd34b73_2600x1385.jpeg 424w, https://substackcdn.com/image/fetch/$s_!SCek!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb15fc2fe-6b8f-4a8f-8590-c72fedd34b73_2600x1385.jpeg 848w, https://substackcdn.com/image/fetch/$s_!SCek!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb15fc2fe-6b8f-4a8f-8590-c72fedd34b73_2600x1385.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!SCek!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb15fc2fe-6b8f-4a8f-8590-c72fedd34b73_2600x1385.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>These timelines are eventually consistent. Periods of inconsistency explode the complexity of the system.</p><ol><li><p>The customer takes out a contract in Kanton Z&#252;rich.</p></li><li><p>At the end of the year, the tax authorities are notified.</p></li><li><p>A week later, &#8220;Oh I forgot to tell you. I moved to Kanton Uri in November.&#8221;</p></li><li><p>Now what?</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vQZw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053d8220-f7c2-42a9-b543-dbf3bdf29028.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vQZw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053d8220-f7c2-42a9-b543-dbf3bdf29028.heic 424w, https://substackcdn.com/image/fetch/$s_!vQZw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053d8220-f7c2-42a9-b543-dbf3bdf29028.heic 848w, https://substackcdn.com/image/fetch/$s_!vQZw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053d8220-f7c2-42a9-b543-dbf3bdf29028.heic 1272w, https://substackcdn.com/image/fetch/$s_!vQZw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053d8220-f7c2-42a9-b543-dbf3bdf29028.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vQZw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053d8220-f7c2-42a9-b543-dbf3bdf29028.heic" width="1456" height="577" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/053d8220-f7c2-42a9-b543-dbf3bdf29028.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:577,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:370439,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vQZw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053d8220-f7c2-42a9-b543-dbf3bdf29028.heic 424w, https://substackcdn.com/image/fetch/$s_!vQZw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053d8220-f7c2-42a9-b543-dbf3bdf29028.heic 848w, https://substackcdn.com/image/fetch/$s_!vQZw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053d8220-f7c2-42a9-b543-dbf3bdf29028.heic 1272w, https://substackcdn.com/image/fetch/$s_!vQZw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F053d8220-f7c2-42a9-b543-dbf3bdf29028.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Delay Introduces Inconsistency, Multiplying Complexity</figcaption></figure></div></li></ol><p>If we want to compute automatically, we need to:</p><ul><li><p>Identify periods of inconsistency</p></li><li><p>Reverse &amp; recompute the business processes that took place during inconsistency</p></li></ul><h2>Bi-temporality</h2><p>Computing automatically requires a more complicated notion of time than the obvious. In the next post I&#8217;ll introduce bi-temporality, which we not only are going to need, but we need it <em>now</em>.</p><p>Software design is not about minimizing design complexity, but rather spending our complexity budget where it can do the most good.</p>]]></content:encoded></item></channel></rss>