<?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"><channel><title><![CDATA[Antman writes software]]></title><description><![CDATA[Former CTO of SKUTOPIA
I write about Functional Programming, TypeScript, Node, Event Sourcing, and tech leadership.
I also write at A Democratic Economy about t]]></description><link>https://antman-does-software.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1636787416357/ziE2OkdF_.png</url><title>Antman writes software</title><link>https://antman-does-software.com</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 18 Apr 2026 23:00:06 GMT</lastBuildDate><atom:link href="https://antman-does-software.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[I Will Never Use AI to Code (or write)]]></title><description><![CDATA[This article was originally published on A Democratic Economy where are I write about an alternative way of doing business likely to result in a new economic paradigm and a better world. Language Warn]]></description><link>https://antman-does-software.com/i-will-never-use-ai-to-code-or-write</link><guid isPermaLink="true">https://antman-does-software.com/i-will-never-use-ai-to-code-or-write</guid><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Wed, 04 Mar 2026 06:53:19 GMT</pubDate><content:encoded><![CDATA[<p><em><strong>This article was originally published on</strong></em> <a href="https://www.democratic-economy.org/why-i-will-never-use-ai-to-code-or-write/"><em><strong>A Democratic Economy</strong></em></a> <em>where are I write about an alternative way of doing business likely to result in a new economic paradigm and a better world.</em> <em><strong>Language Warning:</strong></em> <em>This article is quite informal with a different style compared to how I write for Antman Writes Software. It contains a lot of swearing 😅</em></p>
<p>These days, my stance on AI upsets a lot of people. I decided to write down exactly why I'm perfectly happy pissing off people with my anti-AI conviction, and why I'm unlikely to change my mind. Strap in, this is going to be one hell of a ride 😅</p>
<h2>I fucking enjoy coding</h2>
<p>I actually like writing code. Why would I want to give up something I enjoy? I was explaining AI coding to my Dad, a writer, and asked him:</p>
<p>"Would you write a book if writing was prompting an AI to generate a novel and then giving it feedback, like 'flesh out this character more'?"</p>
<p>"No, because it wouldn't be my writing, it wouldn't be fun?"</p>
<p>"That's exactly how I feel about using AI to code. Maybe the only useful thing about AI coding is making it easy to identify engineers that don't enjoy writing code."</p>
<p>I also fucking enjoy writing. I enjoy editing my writing, I enjoy making it as succinct as possible. I especially enjoy, on occasion, intentionally breaking the rules for effect. Sometimes, I particularly enjoy finding another place to squeeze in <em>yet another</em> <em>superfluous</em> <em><strong>fucking</strong></em> <em>swearword</em>! 😁</p>
<p>I wish I could leave it here, but this isn't reason enough for a lot of people. I guess I'll provide some logical arguments or something, <em>whatever</em>.</p>
<h2>Skill Development and the Illusion of Learning</h2>
<p>A lot of people mistake recognition for recollection, and comprehension for understanding. These are not the same thing. You don't develop skills by reading about them. You have to use them, to process the information, integrate what you've learnt into your existing mental schema, try to use it, make some mistakes, identify contradictions between what you've learnt and your existing mental model and resolve them. You have to do the work for any of this to stick.</p>
<p>The fact is that most of the time, AI isn't helping you do this at all. It's feeding you information that you recognize, not things that you can recall for yourself. By reading about it you're only gaining superficial knowledge of the topic, you won't be able to manipulate the ideas or work with them yourself, not in a meaningful way.</p>
<p>A friend of mine made an excellent point recently, saying</p>
<blockquote>
<p>There’s some super mundane tasks in every profession… but if you don’t do them and understand the reasons why and how they’re done…. You’re missing out on very critical information. They cannot be skipped.</p>
<p>I saw something another designer posted, saying she was hiring and she was really sad. Every portfolio she saw was the same mix and mash up of templates and design assets available online. Or done in one of the many web builders that are basically just a drag and drop interface. She was saying how designers aren’t learning web constraints of any kind, or how to do icons or graphics from scratch. And everything looks the same because nobody knows the constraints to be able to push them, or be creative with them.</p>
</blockquote>
<p>On the other hand, if the AI is "better" than you at a particular topic, then you're incapable of identifying its confident hallucinations, you're incapable of correctly judging the quality of its output. This is much like a company started by founders that lack a particular skill — they have a gaping blind spot in that area and, lacking the requisite skills to judge competence in that field, struggle hiring someone competent. It's an externalised Dunning-Kruger effect.</p>
<h2>Skill Decay And the Outsourcing of Cognitive Effort</h2>
<p>If you are more skilled than the AI in a particular field, then yes you could confidently judge the quality of its work and guide it towards better outcomes. But unless you continue exercising those skills yourself, this coaching role will decay your own skills and judgments. Your skills will trend downwards over time, and as your skills decay you will experience greater frustration the next time you try to use them. This increasing frustration makes outsourcing tasks to AI more tempting, further accelerating the decay of your hard earned skills. Using AI for your profession forms a feedback loop wherein your skills decay while its skills increase, helping the AI provider make you more dependent on it.</p>
<p>Yes, teaching people helps you develop your own skills, but not without continued practice. Teaching in isolation is not enough. Anyway, you aren't really teaching when you are wrangling AI, and definitely not teaching <em><strong>people</strong></em>.</p>
<h2>Skill Collapse And the End of Capability</h2>
<p>If AI requires experts in a field to create training data for it to develop its capability, but using AI causes people's skills to decay, and robs that field of the economics that created those experts, then eventually progress in that discipline will stop. AI destroys the resources required for its own creation and progress. If we stop hiring junior software engineers because we're going to outsource their work to AI, we will never have new senior software engineers. Who's going to write all of the open source code for AI to train on? Won't someone think of the AI children?!</p>
<p>Of course, nothing about this is sustainable! Imagine we kept using AI for coding for the next 10, 15, 20 years, imagine we got rid of all software engineering jobs. With continued training these AI models will increasingly be trained on AI output, despite efforts to filter it out of the training data. So then the models collapse, they increasingly hallucinate and we are increasingly unable to detect it because we lack the expertise to identify it. Now we have no models and no software engineers. AI might not cause just model collapse, it could cause a total skill collapse.</p>
<p>But the really worrying thing is that we don't actually need model collapse for AI to cause a total skill collapse. AI could cause a global skill shortage in just a few years if we continue using it like we are now: pulling up the ladder behind us, robbing industries of new talent, and reducing the number of active experts in a given field, and then the AI industry collapses due to its utterly shoddy economics. Great time to be one of those few remaining active experts, probably a terrible time to be anyone else in society.</p>
<h2>Software Engineering Is a Team Sport</h2>
<p>Software doesn't exist in a vacuum. It can't be divorced from the people that are actively working on it. The software and the people form a symbiotic relationship, they <em><strong>are</strong></em> the emergent system. The code changes the behaviour of software engineers, and software engineers change the code and its behaviour. Every line of code is a liability, it's a potential bug, it's a potential source of confusion, it's potentially wrong. The asset is the understanding that has formed around each line of code, the conversations between teammates, the time spent clarifying what the business or user really wanted. All code that someone on the team didn't write is considered legacy code. Usually, the best thing a software engineer can do is delete code.</p>
<p>AI is a tool that can only produce software liabilities, it is incapable of producing the asset itself. People who haven't written software on a team don't understand this at all. Not all people who have written software on a team even understand this (especially the rockstar-arsehole engineers). The rest of the business thinks that the code itself is an asset, they often think more code is better. Heck, I've even had consultants ask me to report on the number of lines of code in a software system as part of investor due diligence, who then acted like they had discovered some kind of "gotcha" when I reported a number lower than they were expecting. The code is not the asset, typing was never the bottleneck, <a href="https://www.gitclear.com/ai_assistant_code_quality_2025_research">AI makes code bases worse</a>.</p>
<h2>AI and the Case of the Missing Gross Profit, Net Profit, or Any Fucking Profit at All for That Matter</h2>
<p>These things don't fucking make money. Everyone using AI right now is using it based on it being heavily subsidized by Anthropic, OpenAI, VCs, and an enormous, utterly gargantuan, amount of debt. At this point I've read nearly 100,000 words analyzing the financials behind the AI bubble. I strongly recommend reading <a href="https://www.wheresyoured.at/data-center-crisis/">The AI Data Centre Financial Crisis</a> by Ed Zitron who explains this much better than I can.</p>
<p>Let's start by meeting the cast of this god awful tragedy:</p>
<ul>
<li><p><strong>The landlords:</strong> They purchased the land upon which these AI data centers will be built.</p>
</li>
<li><ul>
<li>Played by: <a href="https://www.esig.energy/wp-content/uploads/2025/05/ESIG_LLTF_PresentationLancium.pdf?ref=wheresyoured.at">Lancium</a> and others</li>
</ul>
</li>
<li><p><strong>The Builders:</strong> Contracted to build facilities and power stations for the colocation companies</p>
</li>
<li><ul>
<li>Played by: <a href="https://www.mortenson.com/projects/abilene-data-center-development?ref=wheresyoured.at">Mortenson</a> and others</li>
</ul>
</li>
<li><p><strong>Colocation companies:</strong> They lease the land to build and provide what's called a "powered shell", a facility with power, physical security, and internet connections. Some of these companies are failed Crypto startups clinging to life.</p>
</li>
<li><ul>
<li>Played by <a href="https://ir.applieddigital.com/news-events/press-releases/detail/136/applied-digital-announces-pricing-of-2-35-billion-of?ref=wheresyoured.at">Applied Digital</a> (formally Applied Blockchain), <a href="https://www.investing.com/equities/core-scientific-inc-ratios?ref=wheresyoured.at">Core Scientific</a>, <a href="https://archive.is/OmZzG">Cipher Mining</a>, <a href="https://www.crusoe.ai/resources/newsroom/crusoe-blue-owl-capital-and-primary-digital-infrastructure-enter-joint-venture?ref=wheresyoured.at">Crusoe</a>, and <a href="https://archive.is/S9IdE">TeraWulf</a></li>
</ul>
</li>
<li><p><strong>AI Compute Providers:</strong> These companies allow AI Labs and AI-curious hedgefunds to rent access to GPUs.</p>
</li>
<li><ul>
<li>Played by <a href="https://www.prnewswire.com/news-releases/coreweave-secures-7-5-billion-debt-financing-facility-led-by-blackstone-and-magnetar-302148876.html?ref=wheresyoured.at">CoreWeave</a> , Iren, <a href="https://sherwood.news/markets/nebius-q4-earnings-neocloud-ai-data-center-boom-bubble/?ref=wheresyoured.at">Nebius</a>, Google, Microsoft, and <a href="https://www.wheresyoured.at/haters-guide-oracle/#oracle-and-its-partners-have-not-raised-enough-capital-to-pay-for-its-data-centers-%E2%80%94-costs-will-be-at-least-189-billion-in-total-for-45gw-of-data-centers-at-around-42-million-a-megawatt">Oracle</a>.</li>
</ul>
</li>
<li><p><strong>AI Labs:</strong> Train and provide the models</p>
</li>
<li><ul>
<li>Played by OpenAI (<a href="https://x.com/edzitron/status/2003899014622990559?ref=wheresyoured.at">who have net 360 payment terms with CoreWeave</a>), Anthropic, Google, and Meta.</li>
</ul>
</li>
<li><p><strong>AI service providers:</strong> These companies built their businesses using and repackaging the APIs of AI Labs</p>
</li>
<li><ul>
<li>Played by: Cursor, Windsurf, CodeRabbit, and literally hundreds more</li>
</ul>
</li>
</ul>
<p>These companies are building these AI data centers using close to 1 trillion dollars <em>of debt.</em> Each of these companies have raised enormous and unprecedented amounts of debt In addition to raising capital through equity. Each of these companies poses a serious risk to the other companies above and below it in the AI supply chain, and all of them have taken on this 1 trillion in debt for an industry that has made, allegedly, 37 billion in <em><strong>revenue</strong></em>, $0 in profit.</p>
<p>Threats to this enormous tower of debt include:</p>
<ul>
<li><p>Fluctuations in energy price, e.g. geopolitical conflict</p>
</li>
<li><p>Insurance — Data centers this big have literally never been insured before</p>
</li>
<li><p>Failures in energy storage and transmission</p>
</li>
<li><p>Bad maths: they've taken on debt assuming facilities will be built faster than physically possible, but delaying revenue risks insolvency.</p>
</li>
<li><p>Bad business: many of these companies somehow ignore the fact that NVIDIA continue releasing new chips requiring new infrastructure like the <a href="https://developer.nvidia.com/blog/nvidia-800-v-hvdc-architecture-will-power-the-next-generation-of-ai-factories/?ref=wheresyoured.at">1 MW Kyber racks</a>. These new racks obsolete all of the AI data centers currently being built using the <a href="https://www.tomshardware.com/tech-industry/artificial-intelligence/nvidia-reportedly-boosts-vera-rubin-performance-to-ward-hyperscalers-off-amd-instinct-ai-accelerators-increased-boost-clocks-and-memory-bandwidth-pushes-power-demand-by-500-watts-to-2300-watts?ref=wheresyoured.at">Vera Rubin racks</a>, before these data centers even go online. Which is every AI data centre under construction.</p>
</li>
</ul>
<p>I don't know if <em><strong>anyone</strong></em> will be doing much AI coding by 2030.</p>
<h2>AI and the Void of Accountability</h2>
<p>If you can't judge the quality of AI output, how can you be held accountable for its results? Are we going to use Claude to code flight control systems? Who's accountable if it causes a plane crash? Anthropic? The Product Builder/Prompt Engineer using it to generate a flight control system they were incapable of producing themselves?</p>
<p>The fact is that the companies behind these models are already doing everything they can to avoid accountability for the impact of their AI systems. With chatbots <a href="https://www.theguardian.com/us-news/2025/aug/29/chatgpt-suicide-openai-sam-altman-adam-raine">helping children commit suicide</a>, playing a role in or even encouraging <a href="https://en.wikipedia.org/wiki/Deaths_linked_to_chatbots">multiple deaths</a>, fueling <a href="https://www.psychologytoday.com/au/blog/urban-survival/202507/the-emerging-problem-of-ai-psychosis">AI psychosis</a>, <a href="https://www.bbc.com/news/articles/c0k78715enxo">amplifying disinformation campaigns</a>, and being used to <a href="https://www.theguardian.com/technology/2026/jan/22/grok-ai-generated-millions-sexualised-images-in-month-research-says">sexually harass people including children</a>, these companies will happily continue training their systems on our data while avoiding accountability for the resultant harms.</p>
<h2>Environmental Devastation</h2>
<ul>
<li><p><a href="https://www.theguardian.com/technology/2026/jan/15/elon-musk-xai-datacenter-memphis?ref=wheresyoured.at">Gas Turbines powering data centers in Low Income Areas</a></p>
</li>
<li><p>Increasing energy prices, coal powered shortfall</p>
</li>
<li><p>Building data centers on productive farmland</p>
</li>
<li><p>Heating and cooling, water</p>
</li>
<li><p>Manufacturing GPUs</p>
</li>
</ul>
<p>I'm not sure I can even be bothered writing this section. AI companies are planning/hoping/naively-expecting to bring online 10 gigawatts of total load each year. 10 GW is enough to power every home in London. Adding another London worth of power generation every year is not going to be good for the environment. This should be really fucking obvious!</p>
<h2>An Irrational Economy</h2>
<ul>
<li><p>AI Labs rely on AI Services companies to spend money using their APIs</p>
</li>
<li><p>AI Services Companies rely on other companies to purchase their services</p>
</li>
<li><p>Other companies purchase AI services to reduce their headcount</p>
</li>
<li><p>These other companies rely on consumers purchasing their products</p>
</li>
</ul>
<p>If AI revenue optimism/delusion is predicated on using it to broadly reduce headcount across industries, how would consumers be able to afford their products? Does everyone really think it would only affect the customers of <em>other</em> companies, but not their customers? Their own customers would somehow be spared from AI-induced layoffs? In a sense, it's almost poetic; extracting all of the wealth and consolidating it to the point that there is no liquid money and no further economic exchange really is the ultimate neo-conservative own-goal. Good thing there's no way AI could really put everyone out of a job, except by causing a global financial crisis greater than any we've ever seen.</p>
<p>Let's do some maths 🤓 There are 86 billion neurons in the human brain forming 1 quadrillion (10**15) synapses. At 64 bits per parameter (synapse) thats <code>8 * (10 ** 15) = 8,000,000,000,000,000</code> bytes of memory to run a GPT with an equivalent number of parameters That is 7,450,580 GB or 7,275 TB of RAM. For a single instance of a human brain equivalent! This is 77% of Stargate Alibene! It would require 38,805 NVIDIA Blackwell B200's which would come in 4,760 NVL72s costing 3 million dollars each, for a total of 14.2 billion dollars of GPUs requiring ~1 giga-watt and 678 acres <em><strong>per concurrent human-brain equivalent</strong></em>. And these are supposed to replace white collar workers earning six figures?!</p>
<p>If only <em><strong>someone</strong></em> had thought about this from first principles 🤦‍♂️</p>
<h2>Conclusion: I Don't Want to Use AI</h2>
<p>The thing is that even if I was wrong (I'm not) and AI was somehow helpful for software engineering (it isn't), I still wouldn't want to use it.</p>
<p>Might I be wrong? I don't know, maybe? Might I be left behind in my industry? I guess, but I don't think it's likely. But honestly I would rather be wrong and left behind than spend my days prompting an AI to do my chosen <em><strong>craft</strong></em>, something I love.</p>
]]></content:encoded></item><item><title><![CDATA[Coupling]]></title><description><![CDATA[Coupling is not intrinsically bad. Too little coupling makes systems brittle - changes have to be repeated in multiple places, system behaviour grows increasingly inconsistent, and finding 100% of the code that needs to be updated as part of a change...]]></description><link>https://antman-does-software.com/coupling</link><guid isPermaLink="true">https://antman-does-software.com/coupling</guid><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[architecture]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sat, 30 Aug 2025 07:55:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/0LAJfSNa-xQ/upload/0f24886cde92cfbfcd78274e7cb24396.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Coupling is not intrinsically bad. Too little coupling makes systems brittle - changes have to be repeated in multiple places, system behaviour grows increasingly inconsistent, and finding 100% of the code that needs to be updated as part of a change becomes harder. Too much coupling, and systems become calcified; Updates have unintended consequences, API boundaries become incredibly verbose with large amounts of parameters and options, and figuring out how to update every piece of code impacted by a change becomes increasingly difficult.</p>
<p>Yet even that is too simplistic a mental model of coupling. It's not really about too much or too little, it's about appropriate coupling between layers while aligning the vector of change throughout the code with the vector of change throughout the business/application/package — whatever extrinsic forces motivate software updates. The customer journey, the customer types and categories, the organizational chart &amp; design of a business, the third party vendors and partners, etc — the software architecture needs to align with <em>all</em> of it.</p>
<p>Alignment isn't a lack of coupling, it's the right kind of coupling in the right places. You can't build a skyscraper without cohesion between discrete pieces, whether that is steel through concrete, nuts and bolts, welding, mortar, etc. Without cohesion (coupling) you don't have a skyscraper, you have a pile of rubble. But if everything is coupled you also don't have a skyscraper, instead you have an incredibly expensive piece of abstract art that is utterly impermeable since none of the doors can be opened 😅</p>
]]></content:encoded></item><item><title><![CDATA[Reliable HTTP: Outsmarting the Two Generals with Webhooks]]></title><description><![CDATA[The Two Generals Problem is a mathematical theorem proving that no messaging protocol can reliably ensure that two parties share the same state.
However, some approaches guarantee that two distributed systems will follow an acceptable state progressi...]]></description><link>https://antman-does-software.com/reliable-http-outsmarting-the-two-generals-with-webhooks</link><guid isPermaLink="true">https://antman-does-software.com/reliable-http-outsmarting-the-two-generals-with-webhooks</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[distributed system]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[REST API]]></category><category><![CDATA[webhooks]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Mon, 15 Apr 2024 01:57:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1713146171390/dcccc5ef-6f49-4261-b521-b51e1f2de570.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://en.wikipedia.org/wiki/Two_Generals%27_Problem">The Two Generals Problem</a> is a mathematical theorem proving that no messaging protocol can reliably ensure that two parties share the same state.</p>
<p>However, some approaches guarantee that two distributed systems will follow an acceptable state progression over time. That is, state changes progress linearly and deterministically. It either halts or continues but never enters an unrecoverable state, e.g., missing one message in a series of messages.</p>
<p>One of the most common sources of problems in applications is the misuse of HTTP in machine-to-machine communication. In this article, we will examine system design patterns that significantly improve the reliability of HTTP communication between systems.</p>
<p>The first step to solving these problems is understanding the distinction between a <strong>delivery guarantee</strong> provided by <strong>message producers</strong> and a <strong>processing guarantee</strong> provided by <strong>message consumers</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712391248416/cccb71f2-17a4-4250-bf8b-9dedd709ce28.png" alt class="image--center mx-auto" /></p>
<p>Guarantees come in three flavours:</p>
<ul>
<li><p><strong>Atmost-once</strong>: 0 or 1</p>
</li>
<li><p><strong>Atleast-once</strong>: 1 or more</p>
</li>
<li><p><strong>Exactly-once</strong>: 1</p>
</li>
</ul>
<p>Delivery guarantees are either "atmost-once" or "atleast-once".<br />A processing guarantee could be any one of the three, depending on the delivery guarantee provided by the producer.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712391926036/fcf27691-649f-459e-b920-566eaab03d4e.png" alt class="image--center mx-auto" /></p>
<p>Next, we need to understand that every HTTP request/response cycle is TWO messages over one connection:</p>
<ol>
<li><p>The connection opens</p>
</li>
<li><p>The requester writes their message</p>
</li>
<li><p>The responder writes a reply</p>
</li>
<li><p>The connection closes</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712395675541/bab13ff8-4f20-4def-9bc2-6aa4aad02b2f.png" alt class="image--center mx-auto" /></p>
<details><summary>Caveat: Steps 1 &amp; 4</summary><div data-type="detailsContent"><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection">HTTP Keep-Alive</a> means a connection may be reused for subsequent HTTP Request/Response cycles. However, this detail is irrelevant to the current discussion because the HTTP 1.1 spec still requires two messages, regardless of the behaviour of the transport layer.</div></details>

<p>This means the requester can receive an acknowledgement of their message via the response, but critically, <em>the responder does not know if the requester received and processed their reply.</em></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712983600330/07fb0494-80db-4cee-86dd-0a94980fe8b4.png" alt class="image--center mx-auto" /></p>
<p>In both scenarios depicted above, Service B does not receive a confirmation that the message was processed. At best, it may receive confirmation that the packets were received but not confirmation that Service A successfully processed the message.</p>
<p>This lack of confirmation means sending important information via an HTTP Response is unreliable. Suppose that HTTP Response contains the result of the requester's operation to change the responding system's state. In that case, the requester cannot know for certain if their internal representation of the responder's system state is accurate.</p>
<p>That was a wordful and woefully abstract, so let's look at a concrete example.</p>
<h3 id="heading-implementing-exactly-once-processing">Implementing Exactly-Once Processing</h3>
<p>In this scenario, our system must</p>
<ul>
<li><p>Subscribe a user to a subscription plan, provided they meet the requirements for a subscription, e.g. verified email, active account, etc.</p>
</li>
<li><p>Update a 3rd party billing platform that manages the actual charges, invoicing, etc.</p>
</li>
</ul>
<p>Let's add that in this scenario, the business logic dictates <em>that a user can have zero, one, or many active subscriptions.</em> As you can imagine, this means it is essential that every subscription in the third-party billing platform be recorded in our database.</p>
<p>The code below shows a naive implementation using a single HTTP Request+Response cycle.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> createSubscription = <span class="hljs-keyword">async</span> (userId: <span class="hljs-built_in">number</span>, plan: Plan) =&gt; {
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> userDb.get(userId);
  <span class="hljs-keyword">const</span> { outcome } = validateCreateSubscriptionOperation(user, plan);
  <span class="hljs-keyword">if</span> (outcome === <span class="hljs-string">'SUCCESS'</span>) {
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> billingApi.post(
      <span class="hljs-string">`/plan/<span class="hljs-subst">${plan.id}</span>/subscribe`</span>,
      { userId },
    );
    <span class="hljs-keyword">await</span> dbConnectionPool.query(<span class="hljs-string">`
        INSERT INTO subscriptions    
            (status, external_id, user_id, plan_id)
            VALUES ($1, $2, $3, $4)
        `</span>,
        [<span class="hljs-string">'ACTIVE'</span>, result.subscription.id, userId, plan.id]
    );
  }
}
</code></pre>
<p>What happens if our server is terminated after the HTTP request to <code>billingApi</code>, but before updating the database? Our system won't know the subscription was created, and it won't have the subscription id created by the billing system. Ultimately, neither <code>billingApi</code> nor our system will be able to automatically detect and correct the issue.</p>
<p>For the initial subscription request sent by our system, we are the producers of that message, and we implicitly provide atleast once delivery. If we don't get a response from <code>billingApi</code>, or we fail to process the response, then we (or the user) could try again until we do.</p>
<p>For the response, the <code>billingApi</code> is the producer and it only provides an atmost-once delivery guarantee.</p>
<p>This can cause a serious problem for our users. They might try to subscribe to a plan, receive an error, try again, and then be billed twice each month. When they look at their active subscriptions in our system, they would only see one subscription. If they cancel that subscription, they would continue being billed once a month. Even if our customer support team tried to help this user, they would not be able to see the extra subscription. Only someone with access to the third-party billing system, such as a member of finance, or the billing provider's customer support team, would be able to find the problem. However, even they might not know what they are looking for: they could have to check every subscription in both systems, looking for a subscription that only exists in the billing service.</p>
<details><summary>Most billing providers/payment platforms don't work this way</summary><div data-type="detailsContent">This started out as a completely hypothetical situation. I have never worked with a payment service with such a serious design flaw. However, while writing this article I decided to check Braintree and saw that they don't offer webhooks for the result of any POST request. Flabbergasted, I searched further until I found Stack Overflow answers from <a target="_blank" href="https://stackoverflow.com/a/35467677/2935062">Braintree employees</a> as well as <a target="_blank" href="https://stackoverflow.com/a/45139195/2935062">potential customers</a> (who decided to use Stripe instead) that confirmed what I saw in their documentation.</div></details>

<p>So how do we solve this problem? Luckily, the <code>billingApi</code> service also provides webhooks with an atleast-once delivery guarantee. Following every HTTP request we make to <code>billingApi</code>, they will send a webhook, a HTTP request, to our service with the result of our previous request. Each webhook they send to our service contains a unique <code>messageId</code>. If our service does not acknowlege the webhook by responding <code>200 OK</code> within 5 seconds, they will resend the webhook with the same <code>messageId</code> again until we do.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712494875520/4ac52e19-73c2-4baf-b53a-5c9558072dd1.png" alt class="image--center mx-auto" /></p>
<p>First, we need the <code>createSubscription</code> operation to reduce its scope to only dispatching the request. We will rename it <code>dispatchSubscriptionRequest</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> dispatchSubscriptionRequest = <span class="hljs-keyword">async</span> (userId: <span class="hljs-built_in">number</span>, plan: Plan) =&gt; {
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> userDb.get(userId);
  <span class="hljs-keyword">const</span> result = validateDispatchSubscriptionRequestOperation(user, plan);
  <span class="hljs-keyword">if</span> (result.outcome === <span class="hljs-string">'SUCCESS'</span>) {
    <span class="hljs-keyword">await</span> billingApi.post(
      <span class="hljs-string">`/plan/<span class="hljs-subst">${plan.id}</span>/subscribe`</span>,
      { userId },
    );
  }
  <span class="hljs-keyword">return</span> result;
}
</code></pre>
<p>Instead of receiving the <code>subscription.id</code> from the billing provider via the HTTP Response, the billing provider will send it via a webhook.</p>
<p>Here is a condensed example of how we could handle that:</p>
<pre><code class="lang-typescript">api.post(<span class="hljs-string">'webhooks/billing'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { 
    messageId, 
    subscription: { id, userId, planId } 
  } = req.body;
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> dbConnectionPool.query(<span class="hljs-string">`
      INSERT INTO billing_inbox
      (message_id, subscription_id, user_id, plan_id),
      VALUES ($1, $2, $3, $4)`</span>, 
      [messageId, id, userId, planId]
    );
    res.sendStatus(<span class="hljs-number">200</span>);
    <span class="hljs-keyword">return</span>;
  } <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-keyword">const</span> isExistingMessage = (
      e <span class="hljs-keyword">instanceof</span> DatabaseError 
      &amp;&amp; e.code === DbErrorCodes.UniqueViolation
    );
    <span class="hljs-keyword">if</span> (isExistingMessage) {
      res.sendStatus(<span class="hljs-number">200</span>);
      <span class="hljs-keyword">return</span>;
    }
    res.sendStatus(<span class="hljs-number">400</span>);
    <span class="hljs-keyword">return</span>;
  }
});
</code></pre>
<p>Of course, this isn't production grade code, for the sake of brevity it crosses many levels of abstraction in one function. The critical facts here are that</p>
<ul>
<li><p>there is one database transaction per HTTP Request received by the webhook endpoint — a single <em>statement</em> is a single <em>transaction</em>.</p>
</li>
<li><p>we use a unique constraint on our idempotency key, the <code>message_id</code>, to ensure we only save a message once, even if it is sent multiple times.</p>
</li>
<li><p>we return 200 OK if we receive a message that we had previously saved to our inbox, allowing the webhook producer to resend a message until they successfully record our acknowledgment.</p>
</li>
</ul>
<p>Now we need to process the messages in our inbox, guaranteeing we process each message exactly once. For example:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialiseBillingInboxProcessor</span>(<span class="hljs-params"></span>) </span>{
  (<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">while</span> (lifecycle.isOpen()) {
       <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">await</span> dbConnectionPool.connect();
       <span class="hljs-keyword">try</span> {
           <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">'BEGIN'</span>);
           <span class="hljs-keyword">const</span> { rows: [ message ] } = <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`
             SELECT * FROM billing_inbox
             WHERE processed IS FALSE
             LIMIT 1
             FOR UPDATE SKIP LOCKED`</span>
           );
           <span class="hljs-keyword">if</span> (!message) {
             <span class="hljs-keyword">await</span> wait(<span class="hljs-number">200</span>);
             <span class="hljs-keyword">continue</span>; 
           }
           <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`
              INSERT INTO subscriptions    
                (status, external_id, user_id, plan_id)
                VALUES ($1, $2, $3, $4)
             `</span>, [
             <span class="hljs-string">'ACTIVE'</span>, 
              message.subscription_id, 
              message.user_id, 
              message.plan_id
           ]);

           <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`
             UPDATE billing_inbox
             SET processed = true
             WHERE message_id = $1`</span>,
             [message.message_id],
           );
           <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">'COMMIT'</span>);
       } <span class="hljs-keyword">catch</span> (error) {
           logger.error(
             <span class="hljs-string">'Unexpected error processing billing message'</span>,
             { error },
           );
           <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">'ROLLBACK'</span>);
       } <span class="hljs-keyword">finally</span> {
           client.release();
       }
    }
  })();
}
</code></pre>
<p>What's happening in this code snippet? First, we define a function that is synchronous, but contains an immediately invoked async function expression.</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialiseBillingInboxProcessor</span>(<span class="hljs-params"></span>) </span>{
  (<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">while</span> (lifecycle.isOpen()) {

    }
  })();
}
</code></pre>
<p>This prevents other engineers from accidentally awaiting our infinite loop since it won't resolve until the server begins a graceful shutdown.</p>
<p>Next we begin our transaction and take an update lock on the row we select, while excluding rows that have been locked by another transaction.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> client = <span class="hljs-keyword">await</span> dbConnectionPool.connect();
<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">'BEGIN'</span>);
  <span class="hljs-keyword">const</span> { rows: [ message ] } = <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`
    SELECT * FROM billing_inbox
    WHERE processed IS FALSE
    LIMIT 1
    FOR UPDATE SKIP LOCKED`</span>
  );
</code></pre>
<p>This allows us to run the inbox processor across multiple servers simultaneously while preventing any two servers from ever processing the same message at the same time.</p>
<p>If the query returned no results, we wait 200ms before returning to the top of the while loop and polling for new messages again.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">if</span> (!message) {
  <span class="hljs-keyword">await</span> wait(<span class="hljs-number">200</span>);
  <span class="hljs-keyword">continue</span>; 
}
</code></pre>
<p>Next we process the message by creating a new subscription record, marking the message as processed, and committing our transaction.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`
    INSERT INTO subscriptions    
    (status, external_id, user_id, plan_id)
    VALUES ($1, $2, $3, $4)
  `</span>, [
  <span class="hljs-string">'ACTIVE'</span>, 
  message.subscription_id, 
  message.user_id, 
  message.plan_id
]);

<span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`
  UPDATE billing_inbox
  SET processed = true
  WHERE message_id = $1`</span>,
  [message.message_id],
);

<span class="hljs-keyword">await</span> client.query(<span class="hljs-string">'COMMIT'</span>);
</code></pre>
<p>So in summary, this transaction has three steps:</p>
<ol>
<li><p><strong>Retrieval:</strong> Get and lock an unprocessed message row, ensuring other servers don't process it.</p>
</li>
<li><p><strong>Processing:</strong> Insert the new subscription record.</p>
</li>
<li><p><strong>Commit:</strong> Mark the message as processed and commit the transaction.</p>
</li>
</ol>
<p>Voila! Exactly-once processing for each message. If an error occurs during processing, e.g. the server is terminated unexpectedly or the database connection drops mid transaction, then the transaction will be rolled back and processing the message will be automatically retried. Of course, step two can include many more steps, as long as every database query is part of the transaction.</p>
<p></p><details><summary>In reality, a production-grade implementation is much cleaner</summary><div data-type="detailsContent">The example I gave explodes several layers of abstraction into one large function. At SKUTOPIA, we built a simple framework for this type of processing. Using it can be as simple as passing two functions to our process creator: One function to select a message, and another to process it. Our database layer automatically uses the transaction-bound connection when called within a <code>withTransaction</code> higher order function, so the transaction is managed by our process creator. Error handling, observability, metrics, retries, and alerting are managed by our inbox framework. We can even perform serial (and linear) processing or concurrent processing, depending on the use case.</div></details><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713664478295/2ec7dae3-ec8e-465d-8be0-6e4b525d2dd9.png" alt class="image--center mx-auto" /><p></p>
<p>Now we are much more reliable message consumers because we have implemented an exactly once processing guarantee using the billing provider's webhooks, which provide an atleast once delivery guarantee. There is still a flaw in our system design, which we will look at next.</p>
<h3 id="heading-implementing-atleast-once-delivery">Implementing Atleast Once Delivery</h3>
<p>We have learnt that when a Message Producer implements Atleast Once Delivery with idempotency keys we can then implement Exactly Once Processing as a Message Consumer. But what about the messages <em>we</em> produce?</p>
<p>To implement Atleast Once Delivery we will need to</p>
<ul>
<li><p>Create an outbox supporting multiple destinations and providing a unique idempotency key per message</p>
</li>
<li><p>Add messages to the outbox instead of making API requests directly</p>
</li>
<li><p>Process messages in the outbox by sending them to third parties until we receive a response</p>
</li>
</ul>
<p>In our example service we currently send messages to the third party billing provider when our users call the new subscription endpoint. Currently, a user could make a request, receive an error, then resubmit their request. However, the first request error could be a false negative, meaning the billing provider received the request but something went wrong while sending the acknowledgement, either between their server and our server or between our server and our user.</p>
<p>Now that we get the billing provider's response via a webhook and implemented exactly once processing, the user will be able to see and cancel the duplicate subscription. However, we can make this process much more reliable using an outbox that will</p>
<ul>
<li><p>prevent the duplicate subscription from ever occurring</p>
</li>
<li><p>ensure that every successful call to the new subscription endpoint results in a new subscription in the billing provider's system</p>
</li>
<li><p>significantly increase throughput and reduce latency of the new subscription endpoint</p>
</li>
</ul>
<h3 id="heading-creating-the-outbox">Creating The Outbox</h3>
<p>Let's start by defining the outbox table schema</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TYPE</span> "outbox_message_status" <span class="hljs-keyword">AS ENUM</span> (
  <span class="hljs-string">'pending'</span>,
  <span class="hljs-string">'failed'</span>,
  <span class="hljs-string">'sent'</span>
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "outbox" (
  "id" <span class="hljs-type">UUID</span> <span class="hljs-keyword">PRIMARY KEY</span> <span class="hljs-keyword">DEFAULT</span> gen_random_uuid(), <span class="hljs-comment">-- WARNING: https://www.2ndquadrant.com/en/blog/sequential-uuid-generators/ </span>
  "status" "outbox_message_status" <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-string">'pending'</span>,
  "attempts" <span class="hljs-type">SMALLINT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">0</span>,
  "retryLimit" <span class="hljs-type">SMALLINT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">0</span>,
  "retryWaitPeriodMs" <span class="hljs-type">INT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">1000</span>,
  "createdAt" <span class="hljs-type">TIMESTAMP</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">DEFAULT</span> CLOCK_TIMESTAMP(),
  "updatedAt" <span class="hljs-type">TIMESTAMP</span>,
  "destination" <span class="hljs-type">VARCHAR</span>(<span class="hljs-number">255</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
  "payload" <span class="hljs-type">JSONB</span>
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">INDEX</span> outbox_created_at_pending_idx
  <span class="hljs-keyword">ON</span> "outbox" ("status", "createdAt") <span class="hljs-keyword">WHERE</span> "status" = <span class="hljs-string">'pending'</span>;
</code></pre>
<p>For the sake of simplicity I have used a random UUID as both the primary key and the idempotency key, however random UUID primary keys impact performance. The alternatives are to either use a <a target="_blank" href="https://www.2ndquadrant.com/en/blog/sequential-uuid-generators/">partially sequential UUID generator</a> or use a <code>BIGSERIAL</code> primary key.</p>
<p>This schema is, I hope, fairly intuitive. The novel decisions are:</p>
<ul>
<li><p>All columns preceding <code>payload</code> are fixed length. I mostly ordered these columns for readability and intentionality, but I cannot help playing a little <a target="_blank" href="https://www.2ndquadrant.com/en/blog/on-rocks-and-sand/">Column Tetris</a> to optimise for storage. Please forgive me my vices 😅</p>
</li>
<li><p>using <code>CLOCK_TIMESTAMP</code> instead of <code>NOW</code> for <code>createdAt</code> so that two rows inserted in one transaction do not share a timestamp.</p>
</li>
<li><p>using a <a target="_blank" href="https://www.postgresql.org/docs/current/indexes-partial.html">partial index</a> since we will only query for pending rows.</p>
</li>
<li><p>using <code>"camelCase"</code> for columns to make the database repository easier to write.</p>
</li>
<li><p>not indexing <code>destination</code> because in most systems it won't be very <a target="_blank" href="https://orangematter.solarwinds.com/2018/07/18/what-is-database-index-selectivity/">selective</a> (not many distinct values) AND there won't be many <code>pending</code> rows. Even a backlog of 1,000 pending messages isn't worth indexing.</p>
</li>
</ul>
<p>Here is how we might implement a database repository for this table:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> OutboxRepo = {
  add: <span class="hljs-function">(<span class="hljs-params">msg: OutboxInsert</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;Outbox[<span class="hljs-string">'id'</span>]&gt;;
  getPendingMessage: <span class="hljs-function">(<span class="hljs-params">destination?: OutboxDestination</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;Outbox | <span class="hljs-literal">undefined</span>&gt;;
  incrementAttempts: <span class="hljs-function">(<span class="hljs-params">id: Outbox[<span class="hljs-string">'id'</span>]</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">number</span>&gt;;
  setStatus: <span class="hljs-function">(<span class="hljs-params">id: Outbox[<span class="hljs-string">'id'</span>], status: Outbox[<span class="hljs-string">'status'</span>]</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt;;
};

<span class="hljs-keyword">type</span> Outbox = {
  id: <span class="hljs-built_in">string</span>;
  status: <span class="hljs-string">'pending'</span> | <span class="hljs-string">'failed'</span> | <span class="hljs-string">'sent'</span>;
  attempts: <span class="hljs-built_in">number</span>;
  retryLimit: <span class="hljs-built_in">number</span>;
  retryWaitPeriodMs: <span class="hljs-built_in">number</span>;
  createdAt: <span class="hljs-built_in">Date</span>;
  updatedAt: <span class="hljs-built_in">Date</span> | <span class="hljs-literal">null</span>;
  destination: <span class="hljs-built_in">string</span>;
  payload: Record&lt;<span class="hljs-built_in">string</span>, unknown&gt;;
};

<span class="hljs-keyword">type</span> OutboxInsert = Pick&lt;Outbox, <span class="hljs-string">'destination'</span> | <span class="hljs-string">'payload'</span>&gt; &amp; {
  id?: <span class="hljs-built_in">string</span>; <span class="hljs-comment">// for use cases where the client generates the msg id</span>
  retryLimit?: <span class="hljs-built_in">number</span>;
  retryWaitPeriodMs?: <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> OutboxDestination = <span class="hljs-string">'billingProvider'</span> | <span class="hljs-string">'exampleProvider'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> outboxRepo: OutboxRepo = {
  add: <span class="hljs-keyword">async</span> (msg) =&gt; {
    <span class="hljs-keyword">const</span> { keys, values } = <span class="hljs-built_in">Object</span>.entries(msg)
      .reduce&lt;EntriesOf&lt;OutboxInsert&gt;&gt;(<span class="hljs-function">(<span class="hljs-params">obj, [key, value]</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (value !== <span class="hljs-literal">undefined</span>) {
          obj.keys.push(<span class="hljs-string">`"<span class="hljs-subst">${key}</span>"`</span>);
          obj.values.push(value);
        }
        <span class="hljs-keyword">return</span> obj;
      }, {keys: [], values: []});
    <span class="hljs-keyword">const</span> client = getTransactionAwareClient();
    <span class="hljs-keyword">const</span> queryText = <span class="hljs-string">`
      INSERT INTO "outbox" 
      (<span class="hljs-subst">${keys.join(<span class="hljs-string">', '</span>)}</span>)
      VALUES
      (<span class="hljs-subst">${keys.map((_, i) =&gt; <span class="hljs-string">`$<span class="hljs-subst">${i + <span class="hljs-number">1</span>}</span>`</span>)}</span>)
      RETURNING id
    `</span>;
    <span class="hljs-keyword">const</span> { rows: [row] } = <span class="hljs-keyword">await</span> client.query&lt;Pick&lt;Outbox, <span class="hljs-string">'id'</span>&gt;&gt;(queryText, values);
    <span class="hljs-keyword">return</span> row.id;
  },
  getPendingMessage: <span class="hljs-keyword">async</span> (destination) =&gt; {
    <span class="hljs-keyword">const</span> client = getTransactionAwareClient();
    <span class="hljs-keyword">const</span> { rows } = <span class="hljs-keyword">await</span> client.query&lt;Outbox&gt;(<span class="hljs-string">`
      SELECT * FROM "outbox"  
      WHERE status = 'pending'
        <span class="hljs-subst">${destination ? <span class="hljs-string">'AND destination = $1'</span> : <span class="hljs-string">''</span>}</span>
      ORDER BY "createdAt"
      LIMIT 1
      FOR UPDATE SKIP LOCKED;
    `</span>, destination ? [destination] : []);
    <span class="hljs-keyword">return</span> rows.shift();
  },
  incrementAttempts: <span class="hljs-keyword">async</span> (id) =&gt; {
    <span class="hljs-keyword">const</span> { rows: [row] } = <span class="hljs-keyword">await</span> pool.query&lt;Pick&lt;Outbox, <span class="hljs-string">'attempts'</span>&gt;&gt;(<span class="hljs-string">`
      UPDATE "outbox"  
      SET attempts = attempts + 1
      WHERE id = $1
      RETURNING attempts
    `</span>, [id]);
    <span class="hljs-keyword">return</span> row.attempts;
  },
  setStatus: <span class="hljs-keyword">async</span> (id, status) =&gt; {
    <span class="hljs-keyword">const</span> client = getTransactionAwareClient();
    <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`
    UPDATE "outbox"  
    SET status = $1
    WHERE id = $2
    `</span>, [status, id]);
  },
}
</code></pre>
<p>The code above includes a few common utilities I always use:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> ValueOf&lt;T&gt; = T[keyof T];
<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> EntriesOf&lt;T&gt; = { keys: <span class="hljs-built_in">string</span>[], values: ValueOf&lt;T&gt;[] };
</code></pre>
<p>Plus, <code>getTransactionAwareClient</code> represents a way for database repos to automatically use a transaction-bound database connection if the caller is currently mid-transaction. Typically implemented using <a target="_blank" href="https://nodejs.org/docs/latest-v20.x/api/async_context.html#class-asynclocalstorage">AsyncLocalStorage</a>.</p>
<p>It's worth noting that <code>incrementAttempts</code> does not use <code>getTransactionAwareClient</code>. Whenever we call <code>incrementAttempts</code>, we want that change committed separately and immediately, regardless of the outcome of the caller's subsequent operations. Otherwise, during a failure, our attempt counter could be rolled back with the rest of the operation, defeating the purpose of the column.</p>
<p>I made <code>destination</code> an optional parameter when calling <code>getPendingMessage</code> to support use cases where we must run outbox processors dedicated to particular destinations. Why? Assuming the number of concurrent outbound requests we can make is limited, there are a few possible reasons:</p>
<ul>
<li><p>A destination has higher latency: We prevent our faster destinations being delayed by this slower one by running an outbox processor dedicated to the high latency destination.</p>
</li>
<li><p>A destination is more important to the business: We prevent messages to one destination being queued behind others by running an outbox processor dedicated to the high priority destination.</p>
</li>
</ul>
<h3 id="heading-adding-messages-to-the-outbox">Adding Messages To The Outbox</h3>
<p>While implementing Exactly Once Processing, we created a <code>dispatchSubscriptionRequest</code> function that validated the user's subscription request before sending it to the third-party billing provider. Now that we have an outbox, we will update <code>dispatchSubscriptionRequest</code> to add the request to the outbox:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> enqueueSubscriptionRequest = <span class="hljs-keyword">async</span> (userId: <span class="hljs-built_in">number</span>, plan: Plan) =&gt; {
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> userDb.get(userId);
  <span class="hljs-keyword">const</span> result = validateEnqueueSubscriptionRequest(user, plan);
  <span class="hljs-keyword">if</span> (result.outcome === <span class="hljs-string">'SUCCESS'</span>) {
    <span class="hljs-keyword">await</span> outboxRepo.add({
      destination: <span class="hljs-string">'billingProvider'</span>,
      payload: {
        path: <span class="hljs-string">`/plan/<span class="hljs-subst">${plan.id}</span>/subscribe`</span>,
        body: { userId },
      },
    });
  }
  <span class="hljs-keyword">return</span> result;
}
</code></pre>
<p>What have we changed?</p>
<ul>
<li><p>We replaced the verb in the function name from <code>dispatch</code> to <code>enqueue</code> so callers know this function will not immediately make a request to the provider.</p>
</li>
<li><p>We renamed <code>validateEnqueueSubscriptionRequest</code> to match, but it is otherwise unchanged.</p>
</li>
<li><p>We swapped the HTTP request with a call to <code>outboxRepo.add</code></p>
</li>
</ul>
<p>There were no other changes. Importantly, we still return the <code>result</code> from <code>validateEnqueueSubscriptionRequest</code> so that users are informed if their request does not satisfy our business rules.</p>
<h3 id="heading-processing-the-outbox">Processing The Outbox</h3>
<p>We've created our outbox and added messages to it, now let's start processing them. There are a few things to keep in mind while reading the next code snippet:</p>
<ul>
<li><p>This is not production code: It crosses multiple layers of abstraction. In professional code we would breakup this large function to improve readability, but an article does not allow you to <code>CMD+Click</code> a function to jump to its definition and back.</p>
</li>
<li><p>For concepts I explained earlier in the article, I have encapsulated them in functions. Instead of writing raw SQL we call the outboxRepo, instead of explicitly beginning, committing, and rolling back a transaction I use a <code>withTransaction</code> higher order function to commit on promise resolved and rollback if promise rejected.</p>
</li>
<li><p>At 90 lines long it is our largest snippet so far, but don't worry, we will break it down piece by piece.</p>
</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Resolver = <span class="hljs-function">(<span class="hljs-params">v?: unknown</span>) =&gt;</span> <span class="hljs-built_in">void</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialiseOutboxProcessor</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> destinationApis: Record&lt;OutboxDestination, Axios&gt; = {
    billingProvider: axios.create({
      baseUrl: <span class="hljs-string">'https://api.fictional-payment-platform.com/'</span>,
      transformRequest: [<span class="hljs-function">(<span class="hljs-params">data, headers</span>) =&gt;</span> {
        headers[<span class="hljs-string">'Authorization'</span>] = <span class="hljs-string">`Bearer <span class="hljs-subst">${getBillingAuthToken()}</span>`</span>;
        <span class="hljs-keyword">return</span> data;
      }],
      httpsAgent: <span class="hljs-keyword">new</span> https.Agent({
        keepAlive: <span class="hljs-literal">true</span>,
        noDelay: <span class="hljs-literal">true</span>,
        timeout: <span class="hljs-number">30</span>_000, <span class="hljs-comment">// milliseconds</span>
      }),
    }),
    exampleProvider: axios.create({
      baseUrl: <span class="hljs-string">'https://fictional-service.com/api/'</span>,
      httpsAgent: <span class="hljs-keyword">new</span> https.Agent({
        keepAlive: <span class="hljs-literal">true</span>,
        noDelay: <span class="hljs-literal">true</span>,
        timeout: <span class="hljs-number">10</span>_000, <span class="hljs-comment">// milliseconds</span>
      }),
    })
  } <span class="hljs-keyword">as</span> <span class="hljs-keyword">const</span>;

  (<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> concurrencyLimit = <span class="hljs-number">20</span>;
    <span class="hljs-keyword">const</span> pollingIntervalMs = <span class="hljs-number">200</span>;
    <span class="hljs-keyword">let</span> concurrentRequests = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">while</span> (lifecycle.isOpen()) {
      <span class="hljs-comment">// Each execution of the while loop's code block is called a tick</span>
      <span class="hljs-keyword">let</span> resolveTickContinuationPromise: Resolver = <span class="hljs-function">() =&gt;</span> {};
      <span class="hljs-keyword">const</span> tickContinuationPromise = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolver</span>) =&gt;</span> {
        resolveTickContinuationPromise = resolver;
      });

      <span class="hljs-comment">// DO NOT AWAIT withTransaction!</span>
      withTransaction(<span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">const</span> isAtConcurrencyLimit = concurrentRequests &gt;= concurrencyLimit;
        <span class="hljs-keyword">if</span> (isAtConcurrencyLimit) {
          <span class="hljs-keyword">await</span> wait(pollingIntervalMs);
          <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-keyword">const</span> message = <span class="hljs-keyword">await</span> outboxRepo.getPendingMessage();
        <span class="hljs-keyword">if</span> (!message) {
          <span class="hljs-keyword">await</span> wait(pollingIntervalMs);
          <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-keyword">const</span> attempts = <span class="hljs-keyword">await</span> outboxRepo.incrementAttempts(message.id);
        <span class="hljs-keyword">const</span> isAtRetryLimit = attempts &gt; message.retryLimit;
        <span class="hljs-keyword">if</span> (isAtRetryLimit) {
          <span class="hljs-keyword">await</span> outboxRepo.setStatus(message.id, <span class="hljs-string">'failed'</span>);
          <span class="hljs-keyword">return</span>;
        }

        concurrentRequests++;
        resolveTickContinuationPromise();
        <span class="hljs-comment">// resolving here so the while loop can process another </span>
        <span class="hljs-comment">// message concurrently</span>
        <span class="hljs-keyword">const</span> externalApiClient = destinationApis[message.destination];
        <span class="hljs-keyword">const</span> { status } = <span class="hljs-keyword">await</span> externalApiClient.post(
          message.payload.path,
          message.payload.body,
          {
            headers: {
              <span class="hljs-string">'Idempotency-Key'</span>: message.id
            }
          }
        );
        <span class="hljs-keyword">const</span> wasAcknowledged = status &gt;= <span class="hljs-number">200</span> &amp;&amp; status &lt; <span class="hljs-number">300</span>;
        <span class="hljs-keyword">if</span> (wasAcknowledged) {
          <span class="hljs-keyword">await</span> outboxRepo.setStatus(message.id, <span class="hljs-string">'sent'</span>);
        }
        <span class="hljs-comment">/* DO NOT ADD A CATCH BLOCK!
           If an error is thrown, withTransaction MUST catch, log,
           and rollback.
         */</span>
      }).finally(<span class="hljs-function">() =&gt;</span> {
        concurrentRequests--;
        resolveTickContinuationPromise();
      );

      <span class="hljs-keyword">await</span> tickContinuationPromise;
      <span class="hljs-comment">/* 
        awaiting tickContinuationPromise instead of withTransaction 
        allows the outbox processor to send HTTP requests concurrently
        because tickContinuationPromise resolves before starting the 
        HTTP request while withTransaction resolves once the request 
        and transaction have finished
      */</span>
    }
  })();
}
</code></pre>
<p>Let's break this down!</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialiseOutboxProcessor</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> destinationApis: Record&lt;OutboxDestination, Axios&gt; = {
    billingProvider: axios.create({
      baseUrl: <span class="hljs-string">'https://api.fictional-payment-platform.com/'</span>,
      transformRequest: [<span class="hljs-function">(<span class="hljs-params">data, headers</span>) =&gt;</span> {
        headers[<span class="hljs-string">'Authorization'</span>] = <span class="hljs-string">`Bearer <span class="hljs-subst">${getBillingAuthToken()}</span>`</span>;
        <span class="hljs-keyword">return</span> data;
      }],
      httpsAgent: <span class="hljs-keyword">new</span> https.Agent({
        keepAlive: <span class="hljs-literal">true</span>,
        noDelay: <span class="hljs-literal">true</span>,
        timeout: <span class="hljs-number">30</span>_000, <span class="hljs-comment">// milliseconds</span>
      }),
    }),
    exampleProvider: axios.create({
      baseUrl: <span class="hljs-string">'https://fictional-service.com/api/'</span>,
      httpsAgent: <span class="hljs-keyword">new</span> https.Agent({
        keepAlive: <span class="hljs-literal">true</span>,
        noDelay: <span class="hljs-literal">true</span>,
        timeout: <span class="hljs-number">10</span>_000, <span class="hljs-comment">// milliseconds</span>
      }),
    })
  } <span class="hljs-keyword">as</span> <span class="hljs-keyword">const</span>;
</code></pre>
<p>First we declare our destination API map. By telling typescript that the value assigned to <code>destinationApis</code> must satisfy <code>: Record&lt;OutboxDestination, Axios&gt;</code>, we guarantee that TypeScript will not compile if someone adds a new <code>OutboxDestination</code> but forgets to add an axios instance here.</p>
<p>In our Axios instance configurations we</p>
<ul>
<li><p>Set the <code>baseUrl</code> of the destination</p>
</li>
<li><p>Add an Authorization header with a bearer token for the billing provider</p>
</li>
<li><p>Set some sensible defaults for the HTTPS agent:</p>
<ul>
<li><p><a target="_blank" href="https://nodejs.org/api/net.html#socketconnectoptions-connectlistener"><code>keepAlive: true</code></a> keeps the socket open between the requests, like we talked about earlier. This improves performance if we make multiple requests to the destination within the <code>timeout</code></p>
</li>
<li><p><a target="_blank" href="https://nodejs.org/api/net.html#socketconnectoptions-connectlistener"><code>noDelay: true</code></a> turns off <a target="_blank" href="https://www.extrahop.com/blog/tcp-nodelay-nagle-quickack-best-practices">Nagle's Algorithm</a>, improving performance.</p>
</li>
<li><p><a target="_blank" href="https://nodejs.org/api/net.html#socketsettimeouttimeout-callback"><code>timeout: number</code></a> determines how long the socket will stay open after the last data packet. Tweaking the timeout for different destinations can be beneficial depending on their behaviour, for example, setting a timeout <a target="_blank" href="https://connectreport.com/blog/tuning-http-keep-alive-in-node-js/">shorter than the target's keep-alive timeout</a> can help prevent <code>ECONNRESET</code> errors.</p>
</li>
</ul>
</li>
</ul>
<p>Then we finish the object instantiation with <code>as const</code> to tell TypeScript that the <em>properties</em> in this object are readonly.</p>
<pre><code class="lang-typescript">  (<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> concurrencyLimit = <span class="hljs-number">20</span>;
    <span class="hljs-keyword">const</span> pollingIntervalMs = <span class="hljs-number">200</span>;
    <span class="hljs-keyword">let</span> concurrentRequests = <span class="hljs-number">0</span>;
</code></pre>
<p>We begin another immediately invoked function expression like we did with our inbox, only this time we instantiate a few constants and a counter of running requests. Each concurrent message being sent consumes a database connection, but limiting the number of concurrent requests prevents exhaustion of the connection pool.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">while</span> (lifecycle.isOpen()) {
  <span class="hljs-comment">// Each execution of the while loop's code block is called a tick</span>
  <span class="hljs-keyword">let</span> resolveTickContinuationPromise: Resolver = <span class="hljs-function">() =&gt;</span> {};
  <span class="hljs-keyword">const</span> tickContinuationPromise = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolver</span>) =&gt;</span> {
    resolveTickContinuationPromise = resolver;
  });
</code></pre>
<p>We want our while loop to return to the start of its code block once we are about to start our HTTP request, but allow the processing of the result to continue in the background. This will allow us to send requests for multiple outbox messages concurrently.</p>
<p>However, our while loop is inside an async function, and the retrieval and processing of messages will occur within an anonymous function passed to a <code>withTransaction</code> higher order function, we need an alternative to the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue"><code>continue</code></a> statement that allows us to continue the while loop from within the anonymous function but without preventing it from executing the remaining work. We solve this problem by inverting control: The while loop will wait until the anonymous function tells it that it is ready.</p>
<p>In the snippet above, we create the promise the while loop will wait for, <code>tickContinuationPromise</code>, and we assign the promise resolver to a variable that will be in the anonymous function's closure.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// DO NOT AWAIT withTransaction!</span>
withTransaction(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> isAtConcurrencyLimit = concurrentRequests &gt;= concurrencyLimit;
  <span class="hljs-keyword">if</span> (isAtConcurrencyLimit) {
    <span class="hljs-keyword">await</span> wait(pollingIntervalMs);
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-keyword">const</span> message = <span class="hljs-keyword">await</span> outboxRepo.getPendingMessage();
  <span class="hljs-keyword">if</span> (!message) {
    <span class="hljs-keyword">await</span> wait(pollingIntervalMs);
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-keyword">const</span> attempts = <span class="hljs-keyword">await</span> outboxRepo.incrementAttempts(message.id);
  <span class="hljs-keyword">const</span> isAtRetryLimit = attempts &gt; message.retryLimit;
  <span class="hljs-keyword">if</span> (isAtRetryLimit) {
     <span class="hljs-keyword">await</span> outboxRepo.setStatus(message.id, <span class="hljs-string">'failed'</span>);
     <span class="hljs-keyword">return</span>;
  }

  concurrentRequests++;
  resolveTickContinuationPromise();
  <span class="hljs-comment">// resolving here so the while loop can process another</span>
  <span class="hljs-comment">// message concurrently</span>
</code></pre>
<p>In the snippet above we perform a few checks before sending the request. Just before we send the request we increment the concurrent requests counter and resolve the continuation promise. This allows the while loop to return to the top of its code block while this function continues running in the background.</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">const</span> externalApiClient = destinationApis[message.destination];
  <span class="hljs-keyword">const</span> { status } = <span class="hljs-keyword">await</span> externalApiClient.post(
    message.payload.path,
    message.payload.body,
    {
      headers: {
        <span class="hljs-string">'Idempotency-Key'</span>: message.id
      }
    }
  );

  <span class="hljs-keyword">const</span> wasAcknowledged = status &gt;= <span class="hljs-number">200</span> &amp;&amp; status &lt; <span class="hljs-number">300</span>;
  <span class="hljs-keyword">if</span> (wasAcknowledged) {
    <span class="hljs-keyword">await</span> outboxRepo.setStatus(message.id, <span class="hljs-string">'sent'</span>);
  }
}).finally(<span class="hljs-function">() =&gt;</span> {
  concurrentRequests--;
  resolveTickContinuationPromise();
});
</code></pre>
<p>Here we grab one of the clients we prepared earlier. Crucially, we include the message ID as the <a target="_blank" href="https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/#:~:text=The%20Idempotency%2DKey%20HTTP%20Request%20Header%20Field%20An%20idempotency%20key,retries%20of%20the%20same%20request.">Idempotency Key Header</a> when we make a request. That way the recipient, in this case the billing system, can detect if we have sent this message previously.</p>
<p>Once we get a response, we check the status code, and if they returned 200 we mark the message as sent to prevent sending it again. Then inside the <code>finally</code> callback of the <code>withTransaction</code> promise we decrement the concurrent request counter and resolve the continuation promise in case an error or early return had occurred.</p>
<pre><code class="lang-typescript">      <span class="hljs-keyword">await</span> tickContinuationPromise;
    }
  })();
}
</code></pre>
<p>And now we wrap everything up! We await the continuation promise as the final statement inside the while loop. Then we close the loop, close the immediately invoked function expression, and close the <code>initialiseOutboxProcessor</code> function definition.</p>
<p>Now we have a process that will send messages from our outbox. Of course, this is just one approach of many and it has some trade-offs to be aware of. The biggest one, is that it maintains a open transaction with the database while making the request to the third-party. This might be acceptable if the third-party is a good citizen who sees their messages to an inbox before processing them.</p>
<p>However, if they attempt to process the message before responding with 200 OK, then our transaction will be held open while their system is processing our message. This makes our system extremely vulnerable to their influence. Database connections are not an infinite resource. In this case we would want to change the status of our message to sending and put a system in place to detect messages that have been stuck in the sending status for too long so that they can be resent again. That approach has a few more failure cases, thereby trading simplicity for performance. I decided to keep it simple for this article.</p>
<p>The outbox in this article is unordered, but sometimes we need to send messages in a particular order. In that scenario we can use a cursor to track our progress through an ordered set of messages.</p>
<h2 id="heading-summary">Summary</h2>
<p>Using HTTP, producers can implement an atleast-once delivery guarantee by sending a message repeatedly until the producer receives an acknowledgement from the consumer via a 200 OK response. (Along with a timeout while waiting for an ack)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712414492841/2be0791b-ee54-4d16-ada3-f9ffc90d0cc9.png" alt class="image--center mx-auto" /></p>
<p>These relatively simple messaging guarantees can be built upon to power our more robust network protocols. Yet, many services offering a REST API expect users to mutate data with a HTTP POST request and then receive updated state via the HTTP response. As we just learned, in this context, a HTTP Response is only useful for receiving acknowledgement of our request. It is an unreliable method for receiving the result of our request.</p>
<p>In HTTP, only the requester knows if the message they sent was delivered. If you provide a REST API where other systems can issue changes to your system, always provide webhooks with an atleast-once delivery guarantee and idempotency key.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713145336497/63bffc47-7249-4896-a939-d7d84b86f176.jpeg" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[The Fundamental Problems of Software]]></title><description><![CDATA[As far as I can tell, there are six immutable fundamental problems faced by all commercial software.

Identifying the correct problem to solve

Getting the right specifications to solve the problem

Distributing a shared understanding of the specific...]]></description><link>https://antman-does-software.com/the-fundamental-problems-of-software</link><guid isPermaLink="true">https://antman-does-software.com/the-fundamental-problems-of-software</guid><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[management]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sun, 26 Nov 2023 03:31:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1700969364853/a9afab7d-dae5-4718-95fc-a0ba70cdf542.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As far as I can tell, there are six immutable fundamental problems faced by all commercial software.</p>
<ol>
<li><p>Identifying the correct problem to solve</p>
</li>
<li><p>Getting the right specifications to solve the problem</p>
</li>
<li><p>Distributing a shared understanding of the specifications</p>
</li>
<li><p>Implementing the specifications</p>
</li>
<li><p>Verifying correct implementation</p>
</li>
<li><p>Repeating the above process in the context of refining the problem and specifications over time</p>
</li>
</ol>
<p>The hardest problems are 1, 2, 3, and 6.</p>
<h2 id="heading-1-problem-discovery-amp-definition">#1: Problem Discovery &amp; Definition</h2>
<p>Identifying the correct problem to solve is difficult because human psychology is hard. No matter the nature of the problem, someone only wants us to solve it because of how the person or people purchasing the solution expects that solution will make them feel.</p>
<p>I don’t care what problem you’re solving, at the end of the day you’re working on it because of how one or more people feel about it. “But Anthony, I’m writing software for NASA’s next Mars rover”, you say. Sorry, but we only want it because humans feel curious about the world and perhaps want some greater sense of mastery over an indifferent universe.</p>
<p>Every software project’s greatest risk is a lack of sufficient value. If what we write doesn’t meet a need sufficient for someone to part with their money, our software won’t be run and we probably won’t be paid to write it much longer.</p>
<h2 id="heading-2-solution-discovery-amp-design">#2: Solution Discovery &amp; Design</h2>
<p>The next most challenging aspect of a software project is actually figuring out how to solve the problem. This solution needs to be fit for purpose in multiple dimensions.</p>
<p>It needs to</p>
<ul>
<li><p>be understood and usable for its users (those pesky humans and their psychology again!)</p>
</li>
<li><p>be cost-effective for the purchaser.</p>
</li>
<li><p>be economically viable for the business providing it.</p>
</li>
<li><p>meet the requirements of customer support teams.</p>
</li>
<li><p>meet legal and regulatory requirements.</p>
</li>
<li><p>be feasible to implement.</p>
</li>
<li><p>meet the performance, reliability, and durability expectations of its users.</p>
</li>
<li><p>meet the maintainability, changeability, testability, and observability requirements of the team working on it over the life of the service (human psychology strikes again!)</p>
</li>
<li><p>meet the financial reporting requirements of the business.</p>
</li>
<li><p>meet the business intelligence needs of the business.</p>
</li>
</ul>
<p>All aspects of this multidimensional fit need to be considered across many different time scales. What race condition could occur if two actors issue a command within a few hundred milliseconds? How many requests per minute can it handle? How many months until the database needs to scale? How many years ago was this piece of code last changed?</p>
<p>As humans, we tend to solve problems by thinking in 2D-dimensional Euclidean space. If we can draw it on a screen or a page, we can expand our thought process beyond the limited working memory of our brains. And we can even share it with others. But even if we drew sequence diagrams, entity relationship diagrams, process maps, component diagrams, deployment diagrams, infrastructure diagrams, network diagrams, state machine diagrams, interaction overview diagrams, or even the C4 diagrams — system context, container, component, and code — we still would not be able to verify we have a correct and valuable solution until we run code for human use.</p>
<p>And this brings us to the heart of the third fundamental problem of building software.</p>
<h2 id="heading-3-shared-understanding">#3: Shared Understanding</h2>
<p>Not only is it effectively impossible to perfectly share the correct specification with a team for implementation, but we can’t know how correct our specification is until after we’ve built it!</p>
<p>So the specification will change while we’re implementing it, and every person implementing it will have a slightly different understanding of what the specification even is. Worse still, every person will have a slightly different understanding of the problem the specification is supposed to solve.</p>
<p>That means that while finding the correct problem and correct solution is a matter of human psychology, creating software is a problem of organisational psychology! Software is written, sold, maintained, and improved in a technosocial and psychosocial context.</p>
<p>For this reason, every software leader should endeavour to find ways to keep both their individual teams as small as possible, as well as their total headcount. Interpersonal complexity increases combinatorially.</p>
<p>But this is in direct tension with the fact that creating a defensible business requires building a large system to address the essential complexity of a valuable (difficult) problem. If the problem could be solved by one or two people, it likely already would be.</p>
<h2 id="heading-4-amp-5-implementation-and-verification">#4 &amp; 5: Implementation and Verification</h2>
<p>Funnily enough, problems 4 and 5 are the easiest parts of creating a software solution, yet they’re the focus of most of the literature on creating software. Most books, articles, YouTube videos, and online courses are about implementation. How come? Probably because this part of the problem is easily repeatable, consistent, verifiable, and locally reproducible. I can run a Kubernetes cluster on my local, but not the customer’s mind.</p>
<p>That's not to say it isn't important. The people working on this part, the engineers, must have a thorough understanding of the fundamentals of their craft. The stronger their fundamentals, the more capable they are of reducing a problem to its essence and finding the simplest possible solution.</p>
<h2 id="heading-6-software-over-time">#6: Software Over Time</h2>
<p>Problem 6 is the problem of our solution existing and changing over time. It is the fact that this whole process is recursive. Our software solution begets new problems, requiring us to either change our existing solution or implement new ones. This is where software engineering emerges out of programming over time.</p>
<p>Given that most of the complexity, and all of the value, of a software solution exists <em>after</em> it is delivered, this aspect is vitally important. Yet, humans tend to be bad at predictions and timescales. There is an enormous wealth of information addressing this problem from a technological perspective with engineering solutions, e.g. Site Reliability Engineering, but this problem also consists, perhaps equally, of an anthropological component.</p>
<p>Every technological component forms a feedback loop with the people working on it. The people working on it are in a feedback loop with their adjacent teams, the wider business, and the customers. The impact of supposedly technological choices such as branch protection rules or choosing an infrastructure-as-code (IaC) tool is mostly anthropological over a sufficient timescale. That branch protection rule will become your pull request review culture, which will become your team dynamics and hierarchy. That IaC tool choice might determine whether your teams become siloed.</p>
<h2 id="heading-who-solves-these-problems">Who Solves These Problems?</h2>
<p>When we look at the approach of the people solving these problems, a maturity model emerges. One I see professionals grow through over time. This model consists of three archetypes.</p>
<p>First is the coder, who focuses on problem #4, and a little on #5. Their impact is largely out of their hands, they simply implement the solutions asked of them.</p>
<p>Next is the software engineer, who focuses on problems 4, 5, and 6. They also start thinking about problems 2 and 3, and sometimes problem 1. This person is increasing their impact over time, rather than optimising for the short term.</p>
<p>Finally, we have the product engineer. This person works to solve all six problems. They’re not just leveraging their impact over time, but also in multiple social dimensions. They build trust within their organisation to improve the multidimensional fitness of their solutions by receiving candid feedback, as well as vulnerable expressions of the problems and concerns faced by their colleagues. They also know they need trust across the organisation because aspects of their solution will inevitably be incorrect. Without sufficient trust, they won't get the opportunity to improve their solution. Product engineers aren't just communicating within their group, but also with customers. They know the human context where their solution is used will determine its usefulness. They’re deadly focused on understanding the problem.</p>
<p>Ultimately, the lesson in all of this is that anyone working in software needs to aspire to better understand people. To become a better communicator, to grow their empathy, to become a better person. The technology is just the tool. It's not enough to love our tools, or even the solutions they create. We need to love the people we're building software with, and the people we're building software for.</p>
]]></content:encoded></item><item><title><![CDATA[The Four Quadrants of Complexity]]></title><description><![CDATA[Essential complexity is where software engineers are uniquely able to deliver business value, whereas accidental complexity is what some engineers think looks good on their resumes.  
Another dimension, shown in the diagram below, is frequency. This ...]]></description><link>https://antman-does-software.com/the-four-quadrants-of-complexity</link><guid isPermaLink="true">https://antman-does-software.com/the-four-quadrants-of-complexity</guid><category><![CDATA[management]]></category><category><![CDATA[engineering]]></category><category><![CDATA[software development]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sun, 05 Nov 2023 02:15:36 GMT</pubDate><content:encoded><![CDATA[<p>Essential complexity is where software engineers are uniquely able to deliver business value, whereas accidental complexity is what some engineers think looks good on their resumes.  </p>
<p>Another dimension, shown in the diagram below, is frequency. This ranges from frequently repeated complexities to once-off complexity.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699150037159/c107cd04-8f24-41f0-a1dd-3f9cb4ce9c1f.png" alt class="image--center mx-auto" /></p>
<p>Repeated complexities can be an area of the system that suffers a high rate of change, or it can be a cross-cutting concern that re-appears frequently throughout the codebase.  </p>
<p>These two dimensions give rise to four quadrants:  </p>
<ul>
<li><strong>Repeated accidental complexity:</strong> a time sink where engineering productivity goes to die.  </li>
<li><strong>Once-off accidental complexity:</strong> a necessary evil. You get through it and hope you never need to touch it again.  </li>
<li><strong>Repeated essential complexity:</strong> leaves people feeling good about wasting time. The hardest thing about repeated essential complexity is detecting it, but boredom or attrition of talented engineers might be a sign.  </li>
<li><strong>Once-off essential complexity:</strong> the holy grail of software engineering — it delivers business value with minimal cost and great ROI.  </li>
</ul>
<p>What are the fixes?  </p>
<p>For repeated accidental complexity, you either need to eliminate the complexity entirely (do you really need Kubernetes? No, really, do you?) or create an abstraction, API, or framework that transforms repeated tasks into once-off tasks. But be warned, this only shifts the complexity left. Natural entropic forces push complexity to the right. One day, it could blow up into a time-sink again.  </p>
<p>For once-off accidental complexity, the fix is to leave it alone. Take engineers off it, prioritise and champion other work. Sometimes, the source of the entropic force pushing complexity to the right is your team’s desire to improve things! But if it ain’t broke, don’t fix it.  </p>
<p>Repeated essential complexity first needs to be detected, then, like its accidental cousin, the correct abstraction, API, or framework needs to be created to shift left. But note that you’re paying for this with what you hope is once-off accidental complexity. There are several traps here, such as excessive code reuse leading to inappropriate coupling that makes changes harder.  </p>
<p>For once-off essential complexity, the fix is to focus on solving the problem in front of you. Don’t overcomplicate it. Don’t reach for new tools. Don’t try to prematurely optimise.  </p>
<p>In general, good tooling helps shift all complexity a little left. Whether that’s paying for a proper IDE rather than VSCode, setting up linters, debuggers, or static analysis tools.  </p>
<p>Platform teams are also another solution, but one that many companies reach for too soon. You need many engineers for accidental complexity to cost enough to give platform teams a positive ROI. Buy one too early, and they might start trying to fix stuff that ain’t broke.  </p>
<p>Of course, non-functional requirements introduce a lot of complexity that looks accidental but turns out to be essential or vice versa. Much of technical leadership is helping teams distinguish the two.</p>
]]></content:encoded></item><item><title><![CDATA[Implementing the Outbox Pattern in Nodejs and Postgres]]></title><description><![CDATA[As applications scale, infrequent problems become significant. A network failure for 0.1% of requests is trivial at 1,000 requests per day, but a nightmare for customer support at 1,000,000 requests per day. This commonly happens when we have externa...]]></description><link>https://antman-does-software.com/implementing-the-outbox-pattern-in-nodejs-and-postgres</link><guid isPermaLink="true">https://antman-does-software.com/implementing-the-outbox-pattern-in-nodejs-and-postgres</guid><category><![CDATA[Node.js]]></category><category><![CDATA[event-driven-architecture]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sun, 23 Apr 2023 09:20:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1682240207492/fa732aa4-8699-4977-85e6-1e9befdacf8f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As applications scale, infrequent problems become significant. A network failure for 0.1% of requests is trivial at 1,000 requests per day, but a nightmare for customer support at 1,000,000 requests per day. This commonly happens when we have external dependencies following a successful operation. For example, if we want to send an SMS to a customer after they book an appointment then we will need to:</p>
<ol>
<li><p>Insert their booking information into our database</p>
</li>
<li><p>Send a HTTP request to our SMS service provider</p>
</li>
</ol>
<p>Let's pretend that it is really important that users receive this message, and receive these appointment confirmation messages in the order they were booked.</p>
<p>If we do both actions when a customer sends a request to our <code>appointment/book</code> endpoint, what happens if one fails but the other succeeds? Do we really want to cancel appointments due to latency or temporary errors from an external service? Will we hold open our database transaction until the SMS provider confirms or denies our request? Of course not. We should avoid letting an external service interfere with our application's key value proposition. Holding open database transactions for external services will lead to connection pool exhaustion in our service, especially when the third party's response times increase. Yuck!</p>
<p>How can we ensure that a text message <em>is</em> sent, and retried on failure, after an appointment is booked? By using <a target="_blank" href="https://microservices.io/patterns/data/transactional-outbox.html">The Outbox Pattern</a>. This pattern is especially useful when messages must be dispatched in the correct sequence, such as the payment processor example in the image below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682237742155/2b201002-0b32-4191-bde9-522ca43820f6.png" alt="An example of the outbox pattern used for forwarding events to a payment processor" class="image--center mx-auto" /></p>
<p>In this article, we will use the Outbox Pattern to ensure that an HTTP request to <code>appointment/book</code> only inserts a booking into our bookings table, while a background process sends an SMS message for each record in the table.</p>
<p>What are some of the implementation concerns our solution needs to address? They are:</p>
<ul>
<li><p>Ensuring at least one message is sent per row.</p>
</li>
<li><p>Allowing this background process to run on multiple machines or processes without any risk of sending duplicate messages.</p>
</li>
<li><p>Ensuring messages are dispatched in the order appointments were booked.</p>
</li>
<li><p>Durability in terms of instance termination or process failure. I.e. messages aren't lost because a deployment occurred or a server crashed.</p>
</li>
</ul>
<p>Let's get building!</p>
<h1 id="heading-implementation">Implementation</h1>
<p>For our solution, we are going to use <a target="_blank" href="https://www.typescriptlang.org">TypeScript</a>, <a target="_blank" href="https://nodejs.org/">nodejs</a>, <a target="_blank" href="https://www.npmjs.com/package/pg">pg</a>, and Postgres with <a target="_blank" href="https://www.2ndquadrant.com/en/blog/what-is-select-skip-locked-for-in-postgresql-9-5/">SKIP LOCKED</a>. It will consist of:</p>
<ul>
<li><p>A cursor repository that manages the transactions, locking, updating, and unlocking of a cursor</p>
</li>
<li><p>An outbox process that checks for new records to process</p>
</li>
<li><p>A handler that runs our action for each record</p>
</li>
</ul>
<p>This approach allows us to create multiple outboxes or reuse the code for other purposes such as read model populators in an Event Sourced system.</p>
<h2 id="heading-the-cursor-repository">The Cursor Repository</h2>
<p>For our cursor repository, we want to allow callers to</p>
<ul>
<li><p>Retrieve a cursor for an outbox if another process is not holding a lock on it</p>
</li>
<li><p>Update the cursor value</p>
</li>
<li><p>Unlock the cursor</p>
</li>
</ul>
<p>We want the repository to support multiple processes, multiple cursors, and protect against a few common programmer mistakes. Let's start by defining the cursor table structure:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-string">"cursor"</span> (
     <span class="hljs-string">"process_name"</span> <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> PRIMARY <span class="hljs-keyword">KEY</span>, 
     <span class="hljs-string">"position"</span> <span class="hljs-built_in">BIGINT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">0</span>,
     <span class="hljs-string">"created_at"</span> <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">DEFAULT</span> CLOCK_TIMESTAMP(),
     <span class="hljs-string">"updated_at"</span> <span class="hljs-built_in">TIMESTAMP</span>
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TRIGGER</span> cursor_update_trigger 
 <span class="hljs-keyword">BEFORE</span> <span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">ON</span> <span class="hljs-string">"cursor"</span> 
 <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">EACH</span> <span class="hljs-keyword">ROW</span> 
 <span class="hljs-keyword">EXECUTE</span> <span class="hljs-keyword">FUNCTION</span> set_updated_at();
</code></pre>
<p>Let's insert a row for our SMS cursor</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> <span class="hljs-string">"cursor"</span>
(<span class="hljs-string">"process_name"</span>) <span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'booking_sms_outbox'</span>);
</code></pre>
<p>Now let's write our cursor repository. We're going to instantiate a single instance of a cursor repository per process. Our solution will ensure that we never have multiple instances, and that we do not reuse an instance across multiple cursors. This is important because our repository needs to manage transactions and locking, but we want to hide these details from the caller.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> ProcessName = <span class="hljs-string">'booking_sms_outbox'</span> | <span class="hljs-string">'foobar_populator'</span>;

<span class="hljs-keyword">type</span> CursorRepository = {
   getPositionWithLock(): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">number</span> | <span class="hljs-literal">undefined</span>&gt;;
   setPosition(position: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt;;
   unlockCursor(): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt;;
 };

<span class="hljs-keyword">const</span> CURSOR_REPO_MAP: Record&lt;<span class="hljs-built_in">string</span>, CursorRepository&gt; = {};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCursorRepository</span>(<span class="hljs-params">name: ProcessName</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">CursorRepository</span>&gt; </span>{
   <span class="hljs-keyword">if</span> (CURSOR_REPO_MAP[name]) {
     <span class="hljs-keyword">return</span> CURSOR_REPO_MAP[name];
   }

   <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">await</span> connectionPool.connect();
   <span class="hljs-keyword">let</span> isLocked = <span class="hljs-literal">false</span>;

   CURSOR_REPO_MAP[name] = {
     <span class="hljs-keyword">async</span> getPositionWithLock() {
       <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`BEGIN;`</span>);

       <span class="hljs-keyword">const</span> { rows } = <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`
         SELECT position
         FROM "cursor"
         WHERE "process_name" = $1
         LIMIT 1
         FOR UPDATE SKIP LOCKED 
        `</span>, [name]
       );

       <span class="hljs-keyword">const</span> cursor = rows.shift();

       <span class="hljs-keyword">if</span> (!cursor) {
         <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`ROLLBACK;`</span>);
         isLocked = <span class="hljs-literal">false</span>;
         <span class="hljs-keyword">return</span>;
       }
       isLocked = <span class="hljs-literal">true</span>;

       <span class="hljs-keyword">return</span> <span class="hljs-built_in">Number</span>(cursor.position);
       <span class="hljs-comment">// casting bigint to number means this will work until 2^53-1 which is 9,007,199,254,740,991 -- if we inserted 10 million records per day it would take more than 2 million years to exceed this number.</span>
     },

     <span class="hljs-keyword">async</span> setPosition(position) {
       <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`
         UPDATE "cursor"
         SET "position" = $1
         WHERE "process_name" = $2
        `</span>, [position, name]
       );
     },

     <span class="hljs-keyword">async</span> unlockCursor() {
       <span class="hljs-keyword">if</span> (isLocked) {
         <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`COMMIT;`</span>);
         isLocked = <span class="hljs-literal">false</span>;
       }
     },

     releaseClient() {
        client.release();
     }
   };

   <span class="hljs-keyword">return</span> CURSOR_REPO_MAP[name];
 }
</code></pre>
<p>We've used memoisation to implement a <a target="_blank" href="https://antman-does-software.com/functional-singletons-in-typescript-with-real-use-cases">functional singleton</a>. This means the caller can safely call <code>getCursorRepository('booking_sms_outbox');</code> multiple times but only instantiate one instance of the repository.</p>
<p>Our approach also prevents the caller from mixing up process names between calls. Additionally, we ensure that all calls for a cursor are made using an exclusive database connection. This is critical for our transaction and locking behaviour, just be aware that each of these processes will consume one connection per instance.</p>
<p>Now let's create the background process that uses this cursor repository.</p>
<blockquote>
<p><strong>Pro-tip:</strong> Set a sensible value for <code>idle_in_transaction_session_timeout</code> in the connection config used by the cursor. This will help prevent zombie connections from holding the lock indefinitely. You will also want to ensure you have process lifecylce hooks to unlock any locked cursors in the event of process exceptions, SIGTERM, and SIGINT.</p>
</blockquote>
<h2 id="heading-background-processor">Background Processor</h2>
<p>We want to be able to start a background process that retrieves a cursor, gets new records, and handles each record sequentially. We're going to build a generic processor creation function, and then in the final step, we tie it all together with our handler.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> HasId = { id: <span class="hljs-built_in">number</span> };

<span class="hljs-keyword">type</span> Props&lt;DbRecord <span class="hljs-keyword">extends</span> HasId&gt; = {
  processName: ProcessName;
  retrieveRecords: <span class="hljs-function">(<span class="hljs-params">position: <span class="hljs-built_in">number</span></span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;DbRecord&gt;;
  processRecord: <span class="hljs-function">(<span class="hljs-params">record: Record</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt;;
}

<span class="hljs-keyword">const</span> CURSOR_POLLING_SLEEP_MS = <span class="hljs-number">200</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialiseCursorProcess</span>&lt;<span class="hljs-title">DbRecord</span> <span class="hljs-title">extends</span> <span class="hljs-title">HasId</span>&gt;(<span class="hljs-params">props: Props&lt;DbRecord&gt;</span>) </span>{
  <span class="hljs-keyword">const</span> cursorRepo = <span class="hljs-keyword">await</span> getCursorRepo(props.processName);
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> runTick = <span class="hljs-keyword">await</span> createProcessTicker&lt;DbRecord&gt;(props);
    <span class="hljs-keyword">while</span> (lifecycle.isOpen()) {
      <span class="hljs-keyword">const</span> numProcessed = <span class="hljs-keyword">await</span> runTick(); 
      <span class="hljs-keyword">if</span> (numProcessed === <span class="hljs-number">0</span>) {
       <span class="hljs-keyword">await</span> wait(CURSOR_POLLING_SLEEP_MS);
      }
    }
    cursorRepo.releaseClient();
  } <span class="hljs-keyword">catch</span> (error) {
    logger.critical(<span class="hljs-string">'Terminating server: cursor process error'</span>, {
      processName: props.processName,
      error,
    }); <span class="hljs-comment">// we use structured logging</span>
    cursorRepo.releaseClient();
    <span class="hljs-keyword">await</span> lifecycle.close(<span class="hljs-number">1</span>);
  }
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createProcessTicker</span>&lt;<span class="hljs-title">DbRecord</span>&gt;(<span class="hljs-params">{
  processName,
  retrieveRecords,
  processRecord,
}: Props</span>) </span>{
  <span class="hljs-keyword">const</span> cursorRepo = <span class="hljs-keyword">await</span> getCursorRepository(processName);
  lifecycle.on(<span class="hljs-string">'close'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> cursorRepo.unlockCursor();
  });
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">async</span> (): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">number</span>&gt; =&gt; {
    <span class="hljs-keyword">const</span> position = <span class="hljs-keyword">await</span> cursorRepo.getPositionWithLock();
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> position === <span class="hljs-string">'undefined'</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    }
    <span class="hljs-keyword">const</span> records = <span class="hljs-keyword">await</span> retrieveRecords(position);
    <span class="hljs-keyword">let</span> processedRecords = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> record <span class="hljs-keyword">of</span> records) {
        <span class="hljs-keyword">await</span> processRecord(record);
        <span class="hljs-keyword">await</span> cursorRepo.setPosition(record.id);
        processedRecords++;
      }
    } <span class="hljs-keyword">catch</span> (error) {
      logger.error(<span class="hljs-string">'Could not process record'</span>, {
        processName,
        record,
        error,
      });
    } <span class="hljs-keyword">finally</span> {
      <span class="hljs-keyword">await</span> cursorRepo.unlockCursor();
    }

    <span class="hljs-keyword">return</span> processedRecords;
  }
}
</code></pre>
<p>A lot is going on here so let's break it down.</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">const</span> runTick = <span class="hljs-keyword">await</span> createProcessTicker&lt;DbRecord&gt;(props);
  <span class="hljs-keyword">while</span> (lifecycle.isOpen()) {
    <span class="hljs-keyword">const</span> numProcessed = <span class="hljs-keyword">await</span> runTick(); 
    <span class="hljs-keyword">if</span> (numProcessed === <span class="hljs-number">0</span>) {
       <span class="hljs-keyword">await</span> wait(<span class="hljs-number">100</span>);
    }
  }
  cursorRepo.releaseClient();
</code></pre>
<p>First, we pass in our props and create the function that we want to run every tick. Then we start an infinite loop that will run until the node process receives a SIGTERM thanks to <code>lifecycle.isOpen()</code> using a <a target="_blank" href="https://macklin.me/understanding-and-managing-the-node-js-application-lifecycle">lifecycle manager</a>.</p>
<p>If nothing happened during each tick, we want to wait 100ms before running again. This prevents the process from flooding the nodejs event loop with callbacks in every tick. (<code>wait</code> is simply <code>export const wait = async (ms: number) =&gt; new Promise(resolve =&gt; setTimeout(ms, resolve);</code>).</p>
<p>After the while loop we call <code>cursorRepo.releaseClient();</code> to close the connection to the database. This is required for <code>await pool.end()</code> to resolve during server shutdown.</p>
<p>What about that error handling?</p>
<pre><code class="lang-typescript">  } <span class="hljs-keyword">catch</span> (error) {
    logger.critical(<span class="hljs-string">'Terminating server: cursor process error'</span>, {
      processName: props.processName,
      error,
    }); 
    cursorRepo.releaseClient();
    <span class="hljs-keyword">await</span> lifecycle.close(<span class="hljs-number">1</span>);
  }
</code></pre>
<p>In this case, something pretty drastic has happened, we want to log our errors and shut down the server as quickly as possible.</p>
<p>Moving on to the meat of our implementation, the process ticker and its creator function.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createProcessTicker</span>&lt;<span class="hljs-title">DbRecord</span>&gt;(<span class="hljs-params">{
  processName,
  retrieveRecords,
  processRecord,
}: Props</span>) </span>{
  <span class="hljs-keyword">const</span> cursorRepo = <span class="hljs-keyword">await</span> getCursorRepository(processName);
  lifecycle.on(<span class="hljs-string">'close'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> cursorRepo.unlockCursor();
  });
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">async</span> (): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">number</span>&gt; =&gt; {
    <span class="hljs-comment">// trimmed</span>
</code></pre>
<p>In our <code>createProcessTicker</code> function we retrieve our cursor repo and include it in the closure space of our tick function that we define and return on the next line. I've also registered an onClose handler with our lifecycle manager to ensure any cursors release their locks before node exits.</p>
<p>Let's look at what happens in each tick.</p>
<pre><code class="lang-typescript">    <span class="hljs-keyword">const</span> position = <span class="hljs-keyword">await</span> cursorRepo.getPositionWithLock();
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> position === <span class="hljs-string">'undefined'</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    }
</code></pre>
<p>If we cannot retrieve a position it means another nodejs process, perhaps on another server, has a lock on this cursor at present. In that case, we want to return 0 since we won't be processing any records and wait at least 100ms before trying again.</p>
<pre><code class="lang-typescript">    <span class="hljs-keyword">const</span> records = <span class="hljs-keyword">await</span> retrieveRecords(position);
    <span class="hljs-keyword">let</span> processedRecords = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> record <span class="hljs-keyword">of</span> records) {
        <span class="hljs-keyword">await</span> processRecord(record);
        <span class="hljs-keyword">await</span> cursorRepo.setPosition(record.id);
        processedRecords++;
      }
    } <span class="hljs-keyword">catch</span> (error) {
      logger.error(<span class="hljs-string">'Could not process record'</span>, {
        processName,
        record,
        error,
      }); 
    } <span class="hljs-keyword">finally</span> {
      <span class="hljs-keyword">await</span> cursorRepo.unlockCursor();
    }

    <span class="hljs-keyword">return</span> processedRecords;
</code></pre>
<p>Now that we have some records, we simply initialise our counter then attempt to process each one and update our cursor position each time. We must make sure we always unlock the cursor when finished.</p>
<p>In the next tick, our process will re-attempt to process the record. This is handy if it failed due to reasons such as network error, but we will want to trigger alerts if we receive too many <code>'Could not process record'</code> errors in a given period. It's also a good idea to measure how far behind the latest record each cursor is.</p>
<p>Time to bring this all together for our appointment booking SMS dispatcher.</p>
<h2 id="heading-appointment-booking-sms-dispatcher">Appointment Booking SMS Dispatcher</h2>
<p>All of our process scaffolding is in place, now to build our SMS dispatcher.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initBookingSmsDispatcher</span>(<span class="hljs-params"></span>) </span>{
  initialiseCursorProcess&lt;Booking&gt;({
    processName: <span class="hljs-string">'booking_sms_outbox'</span>,
    retrieveRecords: bookingRepo.getNewBookingsSinceId,
    processRecord: <span class="hljs-keyword">async</span> (booking: Booking) =&gt; {
      <span class="hljs-keyword">await</span> smsProvider.sendBookingConfirmation({ booking });
    },
  });
}
</code></pre>
<p>The observant amongst you will notice the unawaited promise in <code>initBookingSmsDispatcher</code>. This is because we would be awaiting a while loop that only closes when the server is terminating. Accidentally awaiting a call to <code>initialiseCursorProcess</code> can cause the entire server to hang. So not only does <code>initBookingSmsDispatcher</code> nicely encapsulate all of our requirements for our SMS Dispatcher, but it also prevents us from accidentally deploying a goofy change failure bug.</p>
<p>For <code>retrieveRecords</code> we supply <code>bookingRepo.getNewBookingsSinceId</code> which is a repository function to retrieve all new rows since a given id. It might look something like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> INSERT_LATENCY_MS = <span class="hljs-number">5</span>;

<span class="hljs-keyword">async</span> getNewBookingsSinceId(id: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">Promise</span>&lt;Booking[]&gt; {
  <span class="hljs-keyword">const</span> isZero = id === <span class="hljs-number">0</span>;
  <span class="hljs-keyword">const</span> queryText = isZero ? <span class="hljs-string">`
    SELECT * FROM "bookings" ORDER BY "created_at" ASC
`</span> : <span class="hljs-string">`
    SELECT * FROM "bookings"
    WHERE "created_at" &gt; (
      SELECT "created_at" FROM "bookings"
      WHERE "id" = $1
      LIMIT 1
    ) AND "created_at" &lt; (NOW() - INTERVAL '<span class="hljs-subst">${INSERT_LATENCY_MS}</span> milliseconds')
    ORDER BY "created_at" ASC
   `</span>;
  <span class="hljs-keyword">const</span> { rows } = <span class="hljs-keyword">await</span> client.query(
    queryText, 
    isZero ? <span class="hljs-literal">undefined</span> : [ id ]
  );
  <span class="hljs-keyword">return</span> rows.map(bookingRowToBooking);
}
</code></pre>
<p>This prevents the skipping of records that were inserted out of order. There is a race condition in Postgres where sequence numbers are granted to transactions concurrently. This means row 1234 might be inserted after row 1235. Looking up by <code>created_at</code> means we still get row 1234 if we look for events created after 1235.</p>
<p>We also avoid looking up records inserted within the last 5 milliseconds using <code>AND "created_at" &lt; (NOW() - INTERVAL '${INSERT_LATENCY_MS} milliseconds')</code>. The reason for this is that when inserting many rows rapidly, there is no way to guarantee that each row will be available for lookup in the same order as their <code>created_at</code> timestamp.</p>
<p>For example, if I rapidly insert rows A, B, C, D, E, F and perform this lookup at the same time, Postgres could return rows A, C, F. But a moment later, the same query could return rows A, B, C, D, E, F. Why? Because rows B, D, and E may have a larger payload that takes longer to write to disk. Without this minor 5 millisecond latency, the cursor would jump to position F, and rows B, D, and E would never be processed.</p>
<p>Why 5 milliseconds? It depends on the size of your payloads. At SKUTOPIA, our largest payload is &lt;20 kB, and we expect 5ms to work reliably up to ~2,000kB. For us, this is a very acceptable trade off.</p>
<blockquote>
<p><strong>Pro-tip:</strong> Consider a <a target="_blank" href="https://www.crunchydata.com/blog/postgresql-brin-indexes-big-data-performance-with-minimal-storage">BRIN index</a> for your <code>created_at</code> column, depending on the size of your table. Also consider using <code>CLOCK_TIMESTAMP</code> instead of <code>NOW</code> for your <code>created_at</code> column, in case the application ever inserts multiple rows in one statement or transaction.</p>
</blockquote>
<h2 id="heading-when-to-avoid-the-outbox-pattern">When to Avoid the Outbox Pattern</h2>
<p>The Outbox Pattern is not a replacement for a job system. This outbox process will halt if it encounters an unprocessable message. That's a design feature for some high-integrity use cases, but a flaw in others. It also trades off throughput for guaranteed ordering. If you need to send a lot of messages concurrently as fast as possible with no consequence in terms of failure, then this pattern is a poor fit.</p>
<p>For this article's SMS example, I would personally use a job system like <a target="_blank" href="https://github.com/timgit/pg-boss">pg-boss</a>, and push back on our fictional PM's request for sequential SMS messages. Customer communication rarely needs an outbox implementation, it's typically programmatic systems that have trouble receiving messages out of order.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Now that you have the foundations for any cursor-based process to progress through a series of database records, you can use it for any outboxes you might want to create. It also works fantastically with Event Sourced systems, which is where we use it at SKUTOPIA. Each of our domain services uses event sourcing for their application state and publishes events to a PubSub topic for communication amongst services. We also use it to ensure usage events are sent to our payment processor. A critical use-case where at least once delivery and message ordering are both required.</p>
<p>Where might you use an outbox? Let me know in the comments.</p>
]]></content:encoded></item><item><title><![CDATA[Tech’s Surprisingly Bad Math in Team Design]]></title><description><![CDATA[How often have you seen this happen: The business is anxious that the product team is not kicking enough goals. They want better business outcomes faster, so they hire more software engineers.
Makes sense at first glance, right? Engineers write the c...]]></description><link>https://antman-does-software.com/techs-surprisingly-bad-math-in-team-design</link><guid isPermaLink="true">https://antman-does-software.com/techs-surprisingly-bad-math-in-team-design</guid><category><![CDATA[management]]></category><category><![CDATA[team]]></category><category><![CDATA[Design]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sun, 27 Nov 2022 12:33:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1669551735626/6PXuSlkD3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>How often have you seen this happen: The business is anxious that the product team is not kicking enough goals. They want better business outcomes faster, so they hire more software engineers.</p>
<p>Makes sense at first glance, right? Engineers write the code. They’re the final step in value creation for the business. But is that really where the company’s spend has the most leverage? We want to maximise our return on investment, and as we’re about to see, hiring more engineers is one of the most expensive ways to achieve that. Surprisingly, hiring more engineers can actually cause <em>revenue</em> to drop compared to staying the course with the current headcount!</p>
<h2 id="heading-a-primer-on-value-chains">A primer on value chains</h2>
<p>To understand the following analysis, we need to understand value chains. A value chain is every step involved in creating a business’s product. In a manufacturing business, this might be </p>
<ol>
<li>acquiring raw materials, </li>
<li>shipping them to a refinery, </li>
<li>refining them, </li>
<li>shipping the refined materials to a manufacturing plant, and then </li>
<li>manufacturing the final goods.</li>
</ol>
<p>Each step in this process takes its inputs from the previous step, performs some operation on them, and produces outputs that will be the inputs for the following step. Often, upstream improvements in efficiency have a multiplicative effect on the outputs downstream. </p>
<p>So what is the value chain in a software company? It’s roughly</p>
<ol>
<li>Research: Identifying valuable problems to solve</li>
<li>Designing a feasible, viable, valuable, and usable solution</li>
<li>Experimenting to de-risk that solution: Usability testing, tech spikes, wizard-of-oz tests, fake door tests.</li>
<li>Creating the solution via code</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669542415943/_6uKPQgAh.png" alt="Discovery &amp; Delivery.excalidraw-2.png" /></p>
<p>There’s a much simpler way I like to think about this: Every solution you build is a gamble; engineers let you place more bets, and product and design help you choose the bets with the greatest odds of winning.</p>
<h2 id="heading-the-software-value-chain">The Software Value Chain</h2>
<p>Let’s explore each of these steps. Every company does them to some extent, but the quality of execution at each step is not equally distributed amongst software product companies. In companies without a strong product culture, the early parts of the software value chain happen in a haphazard design-by-committee way from various stakeholders.</p>
<h3 id="heading-research-identifying-valuable-problems-to-solve">Research: Identifying valuable problems to solve</h3>
<p>This is the responsibility of Product Managers. Many companies mistake Product Managers for Project Managers. They wind up paying lots of money for someone to spend their time writing tickets and micromanaging smart people. That is not the job of a Product Manager. Their primary responsibility is identifying problems worth solving and then de-risking those solutions.</p>
<p>That de-risking aspect is critical. Solutions are incredibly expensive to implement, and we have a limited number of bets we can place per year on features we hope will have a material impact on the business. But Product Managers cannot and should not do it alone. This brings us to the next step.</p>
<h3 id="heading-designing-a-feasible-viable-valuable-and-usable-solution">Designing a feasible, viable, valuable, and usable solution</h3>
<p>Excellent product design doesn’t happen in a vacuum, and it isn’t a one-and-done process. If you think design is about aesthetics, you have completely misunderstood design. The goal of design is to marry the <em>behaviour</em> of the solution with the customer’s mental models of the problem, the task, and your application. Aesthetics are one tiny component of that incredibly complicated process. During the design phase, designers and product managers work together to address the four risks: </p>
<ul>
<li><strong>Value risk:</strong> Will the solution solve the problem in a way that provides adequate value for the customers and the business?</li>
<li><strong>Viability risk:</strong> Can the business actually execute on this solution? Is the solution defensible and profitable? Do stakeholders in legal, marketing, customer support, and other functional departments have any objections to the solution?</li>
<li><strong>Usability risk:</strong> Will a large enough segment of our customer base be able to understand and use this feature well enough to unlock value?</li>
<li><strong>Feasibility risk:</strong> Are we sufficiently confident that engineering will be able to complete the feature in an acceptable timeframe? Will we be able to meet our non-functional requirements, such as performance?</li>
</ul>
<p>To address these risks, we can use several discovery techniques, such as usability testing, empathy interviews, contextual enquiries, prototyping, tech spikes and proof of concepts, as well as working with stakeholders. The more experiments we can run, the more iterations design can create, the higher our confidence that the solution will provide value, AND the more value that solution will provide to customers and the business.</p>
<p>The critical thing to understand here is that every problem has multiple solutions, each of which has many variations itself. It is 100x cheaper to iterate through these solutions in the design phase than in the implementation phase.</p>
<h3 id="heading-delivering-the-solution">Delivering the solution</h3>
<p>The solution has been de-risked through several design and experiment cycles until we were confident it would achieve our desired business outcomes. Now, we’re ready to build it. Building and releasing software is <em>hard</em>. This is the most expensive and complicated part of the value chain.</p>
<h2 id="heading-optimising-the-software-value-chain">Optimising The Software Value Chain</h2>
<p>Now that we have thought about the entire value chain, let’s think about optimising it. We can’t do that without first considering how much we spend on each phase. For the sake of simplicity, I’m going to say every person gets paid $150,000 per annum, and I will only count the base salary cost. If we have</p>
<ul>
<li>1x Product Manager</li>
<li>1x Product Designer</li>
<li>5x Software Engineers</li>
</ul>
<p>We are spending</p>
<ul>
<li>$150,000 on Product Management</li>
<li>$150,000 on Product Design</li>
<li>$750,000 on Software Engineering</li>
</ul>
<p>Which is $1,050,000 in total. </p>
<p>What does this spending get us? Spending on Product Management and Product Design increases our confidence in our solutions. Spending on Software Engineering increases the number of solutions we can ship each year. Or, to put it another way, spending on Engineering increases the number of bets we can place, and spending on Product increases the likelihood those bets will succeed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669543221333/TGezmVUC6.png" alt="Discovery &amp; Delivery bets.excalidraw-2.png" /></p>
<p>However, there is a critical second-order effect here. As the software engineer-to-designer ratio tips further towards engineering, the workload on Product &amp; Design increases, decreasing their ability to contribute to the solution's success. That is, <strong>spending more on engineering can actually cost you revenue due to a reduction in winning bets.</strong></p>
<p>This is a lose-lose outcome for the business. They spend more to make less revenue. If you double the number of engineers, you don’t get twice as many bets per year because diminishing returns eat productivity pretty hard. But you do create twice as much work for Product, and double the salary spent on engineering, which already accounted for over 70% of expenditure.</p>
<p>Why does this happen? Simply, the Product &amp; Design team have less time per feature for experimenting and iterating. Fewer tests and fewer iterations mean a lower chance of success. </p>
<p>Unfortunately, most companies don’t make time for  Product &amp; Design to do their discovery work <em>at all</em>. They simply build their best guess, which can work if you have plenty of cash to burn finding out what sticks, but in this climate, an ill-advised strategy.</p>
<p><strong>So, what can we do to maximise our successful bets for the least expense? To maximise the company’s profit? </strong></p>
<p>Well, we can’t increase the number of Product Managers per squad. That centralisation of context and stakeholder comms is critical to the role's success. Instead, we can double the number of designers. This is approximately only a 15% increase in expenditure, but it will almost double the capacity for discovery and research.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669543414941/nEndPyd7g.png" alt="Discovery &amp; Delivery spend.excalidraw.png" /></p>
<p>What impact does that have on profitability? Well, I built a <a target="_blank" href="https://docs.google.com/spreadsheets/d/1Elu-ICEXtKDqJF7bDD0zA27rZhG6jByOzQ-Xbm41-IM/edit?usp=sharing">spreadsheet</a> to model this system! It shows that a 15% increase in spending results in over 50% more <em>winning</em> bets. It makes sense; you’ve effectively doubled the quality of the inputs going into engineering for a fraction of the price of the whole value chain.</p>
<p>Feel free to copy my spreadsheet and play with the numbers yourself. If you’re a weirdo like me, it’s actually a lot of fun.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>It seems insane to me that the tech industry has been working on the assumption of one designer per squad for almost 15 years without questioning it. Yet, nearly every designer in tech I speak to is burned out and scrambling to get designs ready for engineering, let alone having time to run proper usability testing and discovery. Instead, the solution most tech companies choose seems to be to ask engineers to do more, and hire more of them. Not only is it addressing the wrong part of the value chain, but it makes the problem worse!</p>
<p>This also comes back to the theory of constraints and bottlenecks. It looks as though engineering is the bottleneck because that is where the most activity occurs. But if you change how you measure the system, from the number of bets placed to the number of bets won, then the bottleneck shifts upstream in the value chain. The bottleneck is product and design.</p>
<p>If we address this problem as an industry, not only might we make much better products and regain some credibility after a glutenous frenzy on VC funding that has come crashing down. We might also make tech companies a great place for product designers to work, reduce the pressure on engineers, and save people from needless burnout and the health risks that come with it. </p>
]]></content:encoded></item><item><title><![CDATA[Belonging & Psychological Safety in Remote Teams]]></title><description><![CDATA[Much of the existing literature regarding team culture assumes a co-located team. It’s not that hybrid or remote teams didn’t exist before the pandemic, but they certainly weren’t the norm. Existing studies consider facets such as desk position, buil...]]></description><link>https://antman-does-software.com/belonging-psychological-safety-in-remote-teams</link><guid isPermaLink="true">https://antman-does-software.com/belonging-psychological-safety-in-remote-teams</guid><category><![CDATA[engineering-management]]></category><category><![CDATA[management]]></category><category><![CDATA[remote]]></category><category><![CDATA[remote work]]></category><category><![CDATA[Remote Teams and Culture]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Mon, 31 Oct 2022 12:13:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1667218318861/bg_blyv2Q.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Much of the existing literature regarding team culture assumes a co-located team. It’s not that hybrid or remote teams didn’t exist before the pandemic, but they certainly weren’t the norm. Existing studies consider facets such as desk position, building architecture, meeting rooms, seating arrangement, body language, physical contact, and team outings. How do we make the existing research relevant for a remote team?</p>
<p>When I joined SKUTOPIA in the middle of the pandemic as a fully remote Tech Lead, I had to figure out how to lead a team on the opposite side of Australia, over 3,000 km away. As the first remote team member, I began growing our Engineering department from 3 to 14. Along the way, I also added Product, UX, Data, and IT departments. Distributed across six cities and three countries, figuring out how to foster psychological safety and belonging on a remote team was not optional. It was do or die.</p>
<h2 id="heading-culture-safety-and-belonging">Culture: Safety and Belonging</h2>
<p>Let’s take a minute to define the most important parts of team culture. Just two things are the primary predictors of both team success and staff retention. It isn’t average intelligence or training; it isn’t free food, foosball machines or ping-pong tables. It’s whether psychological safety and a sense of belonging are prevalent among the group. That’s it.</p>
<p>You <em>will</em> need more than those two things to succeed, but success is not probable without them.</p>
<p>What is psychological safety, and why is it so critical to performance? Psychological safety is whether you consider your social environment to be a hostile one or a safe one. In the workplace, this means we feel like we won’t be punished for speaking up, voicing concerns, suggesting ideas, making mistakes, or asking questions.</p>
<p>Similarly, a sense of belonging is the feeling that we are wanted and accepted in our group, now and into the future. That we can contribute to the group, be recognised for that contribution, and thus maintain our place in the group.</p>
<p>In short, you can’t have psychological safety without belonging, and you rarely find belonging without psychological safety. You <em>must have both</em> for your team to succeed.</p>
<p>Our brains are wired to live in groups; we die on our own. Our amygdala is constantly scanning our social interactions for signs of a threat to our status in the group. If we perceive a threat to our status, our prefrontal cortex is hijacked by the amygdala while our entire focus becomes reaffirming our status.</p>
<p>I can tell you from experience, a perceived threat to your continued place in a group sucks shiitake (that’s the <em>totally scientific</em> term for it). Ironically, this undermines our ability to perform, and in a modern workplace, further weakens our status in the group. When groups cannot offer their members psychological safety, their culture becomes pathological, meaning:</p>
<ul>
<li>People cease cooperating (someone else’s success is a threat to your position)</li>
<li>Those who deliver bad news are punished for it</li>
<li>People attempt to do as little as needed to remain a small target: “It’s not my responsibility!”</li>
<li>Communication and cooperation between groups ceases, and territorialism takes its place</li>
<li>Failures lead to scapegoating and punishment rather than inquiry and learning</li>
<li>New and novel ideas are rejected, and innovation stops completely (too risky)</li>
</ul>
<p>The bad news is that psychological safety and belonging are both temporary. It requires a constant stream of affirming signals, not just an absence of threats. What serves as an affirming signal, then? In short, anything that fosters a sense of belonging. But what are the ingredients of belonging?</p>
<p>First, is that our role in the group is likely to continue, called <strong>future orientation</strong>. These signals indicate that our inclusion in the group is expected to continue into the future. This is why career development plans can help increase retention, but they are a rather clunky belonging signal. We will consider others later in this article.</p>
<p>Second is <strong>energy</strong>. These signals occur when people show enthusiasm for us. If you’ve ever had a boss who avoids one on ones or checks his phone during them, you’ve unfortunately experienced the <em>absence</em> of energy.</p>
<p>Third is <strong>individualisation</strong>, which occurs when we are treated as unique and individual. This is commonly referred to as “feeling seen”. It happens when people have bothered to notice traits that are our own. It could be something as small as your boss asking about your kid’s dance performance over the weekend and remembering their name.</p>
<p>What about the affirming signals for psychological safety?</p>
<p>First is <strong>vulnerability</strong>. A signal that we’re in a safe environment is that others are comfortable being vulnerable. This means we see people freely admitting their mistakes and being supported rather than humiliated or punished. For leaders, it is especially important to model vulnerability.</p>
<p>Second is <strong>embracing bad news</strong>. In a safe environment, bad news flows freely. People don’t have quiet conversations about how to avoid bringing bad news to their boss; they go to their leaders for support.</p>
<p>Third is that <strong>everyone has a voice</strong>. In a safe environment, senior people aren’t expected to talk more than junior people. There aren’t rules governing who can ask questions or offer suggestions.</p>
<p>With all of these signals, frequency matters more than intensity.</p>
<h2 id="heading-strong-signals-in-a-remote-workplace">Strong Signals in a Remote Workplace</h2>
<p>Creating these signals in a remote environment is challenging but not impossible. It requires a little more intentionality; things that may have been intuitive in the office require conscious effort in a remote team. Although, for some of us neurodivergent folk, none of this was ever intuitive. Learning to consciously build the habit of using a particular communication style is a process we are very familiar with.</p>
<p>Let’s run through some of these habits and considerations that foster a great remote culture.</p>
<h3 id="heading-speedy-handovers-leave-your-mic-on">Speedy handovers: Leave your mic on</h3>
<p> The human brain is wired for conversation. It is one of our most extraordinary evolutionary feats, and we take it for granted every day. While turn-taking time between cultures ranges from 200ms to 1,000ms, the processing power our brain devotes to the art of gracefully coordinating conversations is immense.</p>
<p>When we switch speaking roles in under a second, this includes the time it takes our brain to prepare our vocal muscles, change our expression, and begin to speak. Before that, it has to begin planning what we are going to say, which means it has already parsed and processed not only what the other person is saying and what it means but also <em>what the other person is likely to say.</em> </p>
<p>This means our brain has already predicted when the conversational handover will occur long before the person speaking finishes their sentence. As this happens, we begin giving the speaker cues that we are about to take a turn speaking. This can be small sub-vocalisations such as “ah” or “eh” (in all cultures, these are open-mouthed vowels as these take the least effort to form), but we also use facial expressions and body language.</p>
<p>Now consider this in the context of a video call. The participant cannot see all of your body language. The signals they do receive are delayed 300 to 800 milliseconds. If your microphone is muted, they miss the sub-vocal signals. Plus, without conscious effort, you will probably unmute your microphone when you should already be talking, further delaying handover.</p>
<p>All up, we’re adding about 2,000 milliseconds to a process that usually takes 200 milliseconds. Yikes! This is one of the reasons it can feel so exhausting to be in video calls all day. </p>
<p>We can do a few things to improve turn-taking in a video call, but the most effective one is the simplest: <strong>leave your microphone on</strong>.</p>
<p>There are typically two reasons we mute our microphones, either because we’re doing something else, like checking Slack, and don’t want people to hear us, or because we’re not using headphones and our speakers are causing some feedback.</p>
<p>Both of these issues are easy to solve. First, if you’re going to be in a video call, either be present or get the fluck out. I’m prone to distraction myself, so I make my video calls full-screen and mute notifications.</p>
<p>Secondly, if you will be making video calls from your workstation a lot, then invest in good equipment. Buy a decent pair of headphones or earphones and a microphone. Trust me, being understood is worth the effort.</p>
<p>And finally, if the acoustics in your home office are problematic, buy some acoustic treatment or add some large soft furniture. Acoustic treatment is about mass, airflow resistivity, and positioning. The goal is to absorb air particle movement at the point where particle movement is greatest and turn it into friction. This means you want to place acoustic treatment at a one-quarter wavelength from the wall at your lowest target frequency. For speaking, this is 1 kHz, so you want your acoustic treatment at least 8 cm (3 inches) off the wall if possible. I use acoustic insulation mounted in timber frames and covered in cloth, with spacers behind the frame.</p>
<p>Lastly, you want your “active listening” body language to work effectively in a video call. This means ensuring your camera is placed on the screen with the other participants. There’s nothing quite so disconcerting as talking to someone who looks away when you’re speaking because their camera is beside them. I, personally, try to keep the video as close to the top and centre of the screen as possible so that it is as close to eye contact as possible during a video call.</p>
<p>And it almost goes without saying, but include as much of your upper torso in the frame, from an eye-level camera, with good lighting. I’ve never felt terribly connected to disembodied eyebrows or looking up a hairy nostril. </p>
<h3 id="heading-leaders-highlight-your-fallibility">Leaders, Highlight Your Fallibility</h3>
<p>This is relevant in co-located and remote teams, but remote teams need a little more consideration regarding who has witnessed your fallibility. Showing vulnerability is the first step to forming trust; psychological safety cannot exist without trust. For leaders, this means highlighting your mistakes and making others aware of them when they otherwise wouldn’t be. This can be as simple as a Slack message in a team channel announcing that you made a wrong decision on X, and thanks to feedback from person Y, you are now going to do Z. I cannot overstate the effectiveness of broadcasting your failures publicly.</p>
<h3 id="heading-embrace-the-messenger">Embrace The Messenger</h3>
<p>Getting bad news flowing in a co-located team is hard, and bad news is critical to success. It’s even harder in a remote team but simpler in other ways. The key is to set up multiple streams of bad news in different formats. A mixture of pull and push, weekly, fortnightly, bi-quarterly, one-on-one and group, anonymous and identifiable. Each of these different formats brings forth different types of bad news from different people. I’ll run you through each practice I use.</p>
<h4 id="heading-one-on-ones">One on Ones</h4>
<blockquote>
<p>fortnightly, pull, individual, identifiable</p>
</blockquote>
<p>This is the most common approach to seeking out bad news. Make sure you specifically ask your direct reports about the challenges they face, their concerns, the things they have learned, their opinions of their teammates, and their suggestions for improving the team. The people you need to listen to most are the ones who are most reluctant to bring up issues, especially regarding other team members. You need to give them a non-judgmental way of speaking about these topics with open-ended questions.</p>
<h4 id="heading-retrospectives">Retrospectives</h4>
<blockquote>
<p>fortnightly, pull, group, identifiable</p>
</blockquote>
<p>This is almost the group equivalent of a one-on-one. While we don’t use Scrum anymore, we kept the fortnightly retrospectives because it is great for the team to discuss their issues and concerns. I try to stay quiet in these meetings. Our job as a leader in a retro isn’t to solve problems. It is to understand them. The team will come up with actions to solve the short-term problems. You need to pay attention to the systemic causes. Solving these issues goes beyond the retro.</p>
<p>My rule of thumb during a retro for anyone in a leadership position is that they must ask at least two questions before offering a solution for a given topic. And be very cautious offering any “solution” that isn’t a concrete action anyone on the team could accomplish before the next retro. I’ve seen too many aspiring leaders completely undermine themselves in retros by offering a so-called “solution” that really was a suggestion that the complainant themselves are the problem, and they should adjust their expectations. Not good, at all. </p>
<p>(If someone really does need to adjust their attitude, the retro is <em>not</em> the place for that conversation.)</p>
<p>Keep an eye on who is contributing the most to a retro, and who isn’t. It’s worth explicitly asking for input from quieter participants from time to time. One cause of an imbalanced retro is that the most frequent speaker is actually speaking on behalf of others. 
A large imbalance implies there is a trust issue in your team. One person is exceptionally safe, and quite likely, one person in particular is making other people feel threatened. People are afraid to speak up in front of the threat, so they channel their concerns through the safe person. This is a big red flag for an imminent issue that could otherwise fly under the radar on a remote team. Start investigating!</p>
<h4 id="heading-team-health-checks">Team Health-Checks</h4>
<blockquote>
<p>bi-quarterly, pull, group, identifiable</p>
</blockquote>
<p>Every six weeks, each of our squads runs a health check session. The metrics we use for the health checks are factors that the team decided were critical to their success. It is their opportunity to hold leadership (<em>me</em>) accountable for giving them what they need. I reiterate that at the start of every session, and then shut my mouth, letting someone else facilitate the session.</p>
<p>We also make a point that the score we record for each factor is <em>the lowest score</em>, not the mean or mode. We’re a team, and that means that any one person suffering is a loss to all of us. We’re all responsible for helping each other; we can’t let our teammates set themselves alight to keep the others warm.</p>
<h4 id="heading-anonymous-surveys">Anonymous Surveys</h4>
<blockquote>
<p>ad-hoc, pull, group and one-on-one, anonymous</p>
</blockquote>
<p>I will regularly put together anonymous surveys on hot topics for the team. This might include communication preferences, tooling state, psychological safety, etc. Anonymous surveys let the team be more candid than they otherwise might be. Be careful; if your anonymous surveys are a lot more negative than your retros or one on ones would suggest, it is likely that your team don’t feel safe speaking their minds.</p>
<p>If your team uses Google Workspaces, these are easy to run using Google Forms with a single response per person while maintaining anonymity. </p>
<p>I refer to surveys as a group and one-on-one channel because the quantitative data is group, while the qualitative data can be individualised. Include a mix of both in your surveys, through Likert-style questions and open-ended questions.</p>
<h4 id="heading-the-insider">The Insider</h4>
<blockquote>
<p>bi-quarterly, pull, one on one, anonymous</p>
</blockquote>
<p>I will regularly have a peer on the team conduct one-on-ones with the team members themselves. By speaking among equals rather than with their direct manager, they may feel safer raising other issues. This can be especially true regarding interpersonal conflicts. The time to resolve interpersonal conflicts is as soon as possible, but people generally want to avoid conflict and are particularly averse to going to their manager with “petty” issues.</p>
<p>I will then have my insider report back to me with the general zeitgeist of the team. This is about discovering the issues, not who said what. Confidentiality is essential for the team’s confidence and the ongoing success of the insider.</p>
<h4 id="heading-guild-slack-channels">Guild Slack Channels</h4>
<blockquote>
<p>continuous, push, group, identifiable</p>
</blockquote>
<p>We have guild channels for engineering, product, and design. In many instances, people will raise concerns they have about process, tooling, or workflow. We also have team channels. The hard part is getting the ball rolling with suggestions. If the team doesn’t have people who feel comfortable doing this already, then you need to ask questions and solicit feedback. After doing this enough times, people will begin raising their questions, concerns, and ideas.</p>
<h4 id="heading-the-missing-element">The Missing Element</h4>
<p>Astute readers will notice a missing combination: continuous, push, individual, and anonymous. This would be the digital equivalent of an anonymous suggestions box. I haven’t personally implemented this combination myself, but it could be feasible with a repeatable anonymous form and a regular Slackbot reminder to solicit contributions. If you try it, let me know how it goes!</p>
<p>Now, that’s enough on the topic of soliciting bad news. Let’s get back to our contributing factors for a healthy remote culture.</p>
<h3 id="heading-over-the-top-gratitude">Over-the-top Gratitude</h3>
<p>Showing gratitude and appreciation on a remote team can be as varied and fun as it is with a co-located team. But as with anything remote, it requires a little more conscious effort.</p>
<p>Everyone has different ways they like to receive praise. I ask new direct reports if they are comfortable receiving praise in public. Occasionally, someone will prefer not to be called out in front of the company for their awesome achievements!</p>
<p>There are a few things to consider when giving praise remotely, they are:</p>
<ul>
<li>Medium</li>
<li>Tone</li>
<li>Audience</li>
</ul>
<p>The mediums being chat, email, video call, voice call, pre-recorded or live. Tone is a question of formal or informal, and audience is, well, the audience.</p>
<p>For example, celebrating someone's success in the company-wide announcements channel will need a different approach than in a video call with their squad, and so on. The guidelines roughly work out like this:</p>
<ul>
<li>The faster the medium, the less formal the tone — Slack is less formal than email</li>
<li>The larger the audience, the more formal the tone — A Slack message in the company-wide channel might be equally formal as an email to the squad.</li>
</ul>
<p>There’s another underlying factor, but company-wide praise of your direct reports can seem like a political game of optics rather than genuine praise. Context and tone are essential here. And remember earlier when I said individualisation was a signal for belonging? It’s harder to individualise your message when sending it to a wider audience.</p>
<p>For this reason, my favourite way to celebrate people’s success is in a Slack channel with their primary team. This allows me to go all out, using some ALL-CAPS, emoji bombs 🎉🤩🕺, an excessive number of exclamation marks (!!!), and some insider references or jokes.</p>
<p>This might sound over the top, too informal, <em>too vulnerable</em>. That is why it works. Text communication needs the extra oomph, but also, when you’re celebrating success, you need to be a person, not a boss. Done right, it’s a key moment for bonding the whole team because it creates a moment of shared pride.</p>
<p>I cannot stress enough how important it is for the people observing you giving praise to someone else, to feel joy themselves; To empathetically feel and share that sense of pride. </p>
<p>Imagine going to someone’s graduation ceremony. Is the healthy response beaming with pride, or seething with jealousy? Healthy teams experience the former when celebrating each other’s success. So many of these rituals we have as humans aren’t about celebrating the individual’s success as much as they are for the group to bond over a sense of shared pride.</p>
<p>In a healthy remote culture, this means your celebratory message should receive many emoji reactions and enthusiastic replies. A dry, formal congratulatory email is not likely to elicit a positive emotional response from its audience. But an over-the-top, emotion-laden, emoji-bombed message has a certain infectious joy! </p>
<p>As important as that asynchronous Slack message is, you need to back it up with <em>another</em> congratulatory moment in a video call with the team. This is critical; we need <em>both</em> the written form and the body language of a video call. Neither on their own is quite enough in a remote team. I also back it up with more congratulatory praise in our next one-on-one. There’s no such thing as too much gratitude.</p>
<p>If you can, sending gifts can also help your team seem real and connected. This could be tasty treats, plushies, vouchers, books (not work-related!), or whatever you think will be appreciated. If you’re lucky, the team will adopt this practice themselves, sending each other gifts to show appreciation. This hits a belonging double-whammy of energy AND individualisation.</p>
<p>As important as big moments of recognition are, it's important to have plenty of little moments of recognition too. I recommend setting up a #high-fives Slack channel where anyone can publicly show gratitude for people who look out for their teammates, whether small or big.  </p>
<h3 id="heading-manufacturing-collisions">Manufacturing Collisions</h3>
<p>Co-located workplaces lead to many accidental collisions. A collision is a random encounter that winds up being beneficial in terms of either information sharing, belonging, or psychological safety. It can be a conversation in the elevator, grabbing a coffee, or overhearing a conversation near your desk.</p>
<p>In a remote team, accidental collisions happen almost exclusively in chat. The rest we need to consciously plan for. I’ve tried each of the following ideas to varying degrees of success with different teams.</p>
<h4 id="heading-coffee-run-slack-huddles">Coffee-run Slack Huddles</h4>
<p>The morning coffee run can be a great bonding experience in the office, but what is its equivalent on a remote team? One method I’ve tried is a regular slack huddle. It has had varying success, but I think my approach with this technique fell into a negative network effect. I recommend trying with small groups in the same time zone. Even better if you can actually consume a hot beverage at the same time. </p>
<h4 id="heading-donut-random-one-on-ones">Donut: Random one-on-ones</h4>
<p>Donut is a Slack app that pairs everyone up together randomly every week or so for a “virtual coffee”. Some people will love having time to get to know their colleagues and talk about things other than work. Others will loathe it.</p>
<h4 id="heading-gather-avatar-proximity-video-chat">Gather: Avatar Proximity Video Chat</h4>
<p>Apps like Gather aim to mimic the spatial element of real-world conversations. Everyone has an avatar in a 2D world, but you can only hear and see the people standing near you. This allows people to drop in and out of conversations, break into groups, and swing by your desk.</p>
<p>I’ve used this for two purposes: mimicking the office and socialising. It works to varying degrees for either purpose, but again, it resonates with some people but not others.</p>
<h4 id="heading-standups-a-double-edged-sword">Standups: A Double-Edged Sword</h4>
<p>We dropped stand-ups in favour of asynchronous status updates. Then we returned to doing them three times a week in groups of less than six people. The larger the group, the less each person talks relative to the length of the meeting. On a video call, this is even worse.</p>
<p>We returned to stand-ups, not for productivity, but because we felt socially unmoored. It was possible to go an entire week without seeing the face of one of your squad mates.</p>
<p>Generally, I would say never use meetings for status updates, but this is one exception. Just keep the number of attendees low and the meeting very short! Anything bigger will not scale with the team.</p>
<h3 id="heading-pick-up-the-digital-trash">Pick Up The (digital) Trash</h3>
<p>Remote teams still have necessary but mundane tasks. As a leader, do them! After we split our Jira board into two projects, we wound up with both teams’ tickets in the new board, and all past and present tickets were reset to to-do and turned into tasks! I reviewed nearly 500 tickets individually and deleted roughly 400 of them. Just like cleaning up around the office, it shows that no one is above caring for the team and doing what needs to be done.</p>
<h3 id="heading-leverage-threshold-moments">Leverage Threshold Moments</h3>
<p>Onboarding on a remote team can be even more daunting than in person in some ways. That means you need to perfect every single element of the experience. If you have flashy promotional videos, include them in your onboarding docs. Set up plenty of one-on-ones with different team members. Don’t be afraid to use over-the-top language and raise the emotional intensity a little, be cheesy and fun, with some self-awareness of your awkwardness.</p>
<p>This is also an excellent moment for connecting their role to a sense of purpose. It can be challenging, but you must connect their work to the company’s mission. In a physical office, you can cover your walls with purpose and symbolism. In a remote team, you need videos, messages, and other constant reminders, rather than relying on ambient indicators of purpose.</p>
<p>Bring together groups of teammates for get-to-know-you video calls instead of team lunches. Prepare some icebreakers; the more awkward they are, the better. I find five people is the most you can have on a call before turn taking breaks down and people begin to disengage. You have to work harder to turn the abstract concept of the remote team into a concrete experience, but it is definitely achievable.</p>
<h2 id="heading-bringing-it-all-together">Bringing It All Together</h2>
<p>No two teams are the same. In fact, someone once said to me, “teams are immutable”, meaning that every time someone leaves or joins a team, it’s really a whole new team. While analogies are never entirely true, this one holds some truth.</p>
<p>What has or hasn’t worked for me may work for you and your team. It might even work for my team at some point in the future when it hasn’t in the past. These ideas are worth trying at least once, if not twice. </p>
<p>That said, the fundamentals never change. Belonging will always require a steady stream of future orientation, energy, and individualisation. Psychological safety will always require vulnerability, open discussion of bad news, and everyone’s voices being heard.</p>
<p>I hope this article has given you some food for thought, and as always, I would love to hear what has worked for you in the comments. What are <em>your</em> techniques for fostering psychological safety and belonging in remote teams?</p>
]]></content:encoded></item><item><title><![CDATA[Measuring Apdex from access logs in SumoLogic]]></title><description><![CDATA[Application Performance Index (Apdex) is a standardised method for calculating the perceived satisfaction of a user accessing your service. It divides all served requests into three categories: satisfied, tolerating, and frustrated.
A user's request ...]]></description><link>https://antman-does-software.com/measuring-apdex-from-access-logs-in-sumologic</link><guid isPermaLink="true">https://antman-does-software.com/measuring-apdex-from-access-logs-in-sumologic</guid><category><![CDATA[logging]]></category><category><![CDATA[infrastructure]]></category><category><![CDATA[Devops]]></category><category><![CDATA[sumologic]]></category><category><![CDATA[apdex]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Thu, 18 Aug 2022 09:30:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/k5uXZniydCg/upload/v1660814947098/5nbzsVb3Q.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Application Performance Index (Apdex) is a standardised method for calculating the perceived satisfaction of a user accessing your service. It divides all served requests into three categories: satisfied, tolerating, and frustrated.</p>
<p>A user's request is said to be satisfied when it occurs within some <code>T</code> value, such as 400ms, and is successful, e.g. 2xx or 3xx status codes.</p>
<p>A tolerating request is successful in more than <code>T</code>, and less than <code>4T</code>.</p>
<p>Frustrated requests exceed <code>4T</code> or fail, e.g. 4xx and 5xx status codes.</p>
<p>So how can we build this measure in SumoLogic? Let's take a look</p>
<pre><code><span class="hljs-operator">|</span> json auto field<span class="hljs-operator">=</span>raw_log
<span class="hljs-operator">|</span> <span class="hljs-keyword">if</span>(statusCode matches <span class="hljs-string">"2*"</span>, <span class="hljs-keyword">if</span>(responseTime <span class="hljs-operator">&lt;</span><span class="hljs-operator">=</span> {{Apdex_Time}}, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>), <span class="hljs-number">0</span>) <span class="hljs-keyword">as</span> satisfied_counter
<span class="hljs-operator">|</span> <span class="hljs-keyword">if</span>(statusCode matches <span class="hljs-string">"2*"</span>, <span class="hljs-keyword">if</span>(responseTime <span class="hljs-operator">&lt;</span> {{Apdex_Time}} <span class="hljs-operator">*</span> <span class="hljs-number">4</span> <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> responseTime <span class="hljs-operator">&gt;</span> {{Apdex_Time}}, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>), <span class="hljs-number">0</span>) <span class="hljs-keyword">as</span> tolerating_counter
<span class="hljs-operator">|</span> timeslice <span class="hljs-number">150</span> buckets
<span class="hljs-operator">|</span> count <span class="hljs-keyword">as</span> total_logs, sum(satisfied_counter) <span class="hljs-keyword">as</span> satisfied, sum(tolerating_counter) <span class="hljs-keyword">as</span> tolerating by _timeslice
<span class="hljs-operator">|</span> ((satisfied<span class="hljs-operator">+</span>tolerating<span class="hljs-operator">/</span><span class="hljs-number">2</span>)<span class="hljs-operator">/</span>total_logs)<span class="hljs-keyword">as</span> apdex
<span class="hljs-operator">|</span> fields apdex, _timeslice
</code></pre><p>We use structured logging, so our logs are JSON formatted, but you could do this just as easily via a regex capture on apache style access logs to extract the status code and response time.</p>
<p>This simply creates a counter for satisfied and tolerating using nested <a target="_blank" href="https://help.sumologic.com/05Search/Search-Query-Language/Search-Operators/if-operator-and">if</a> functions with the <a target="_blank" href="https://help.sumologic.com/05Search/Search-Query-Language/Search-Operators/matches">matches</a> operator. The frustrated queries are everything not captured by these two counters, so <code>count as total_logs</code> gives us everything else we need, assuming our log source only contains access logs.</p>
<p>And that's it! You can even overlay the percentage tolerating, frustrated, and satisfied if you like:</p>
<pre><code><span class="hljs-operator">|</span> ((satisfied<span class="hljs-operator">+</span>tolerating<span class="hljs-operator">/</span><span class="hljs-number">2</span>)<span class="hljs-operator">/</span>total_logs)<span class="hljs-keyword">as</span> apdex
<span class="hljs-operator">|</span> satisfied<span class="hljs-operator">/</span>total_logs <span class="hljs-keyword">as</span> satisfied_pct
<span class="hljs-operator">|</span> tolerating<span class="hljs-operator">/</span>total_logs <span class="hljs-keyword">as</span> tolerating_pct
<span class="hljs-operator">|</span> (total_logs <span class="hljs-operator">-</span> satisfied <span class="hljs-operator">-</span> tolerating)<span class="hljs-operator">/</span>total_logs <span class="hljs-keyword">as</span> frustrated_pct
<span class="hljs-operator">|</span> fields apdex, _timeslice, satisfied_pct, tolerating_pct, frustrated_pct
</code></pre>]]></content:encoded></item><item><title><![CDATA[Applying Google's Testing Methodology to Functional Domain-Driven Design For Scalable Testing]]></title><description><![CDATA[Recently I wrote an article about applying Functional Programming to Domain-Driven Design. One of the key benefits of that approach is improved testability, but we didn't get to delve into it too deeply. 
In this article, we will consider what factor...]]></description><link>https://antman-does-software.com/applying-googles-testing-methodology-to-functional-domain-driven-design-for-scalable-testing</link><guid isPermaLink="true">https://antman-does-software.com/applying-googles-testing-methodology-to-functional-domain-driven-design-for-scalable-testing</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Testing]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Functional Programming]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sun, 08 May 2022 10:30:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/a7B-lCt1fo4/upload/v1652005605238/6IMu0odKr.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently I wrote an article about applying <a target="_blank" href="https://antman-does-software.com/functional-domain-driven-design-simplified">Functional Programming to Domain-Driven Design</a>. One of the key benefits of that approach is improved testability, but we didn't get to delve into it too deeply. </p>
<p>In this article, we will consider what factors make an automated test suite great. We will bring together a lot of ideas from <a target="_blank" href="https://www.amazon.com/Software-Engineering-Google-Lessons-Programming-ebook-dp-B0859PF5HB/dp/B0859PF5HB/">Software Engineering at Google</a>. Whenever I refer to "Google" in this article, I am referring to the authors' depiction of Google's engineering practices in the book.</p>
<p>I will also translate some of Google's testing methodology to TypeScript. We will then see how it fits with Functional Domain-Driven Design (fDDD).</p>
<h1 id="heading-good-test-bad-test">Good Test, Bad Test</h1>
<p>Let's start by defining what a good or bad test suite is.</p>
<p>There are five dimensions we can use to consider the quality of our tests. These are brittleness, flakiness, speed, and readability. In the same sense that optimising outside the bottleneck is wasteful, the metric a team needs to focus on improving is whichever one is worst at any given time.</p>
<h3 id="heading-brittleness-durability">Brittleness (Durability)</h3>
<p>A test is considered brittle when it breaks due to unrelated changes. If you have ever changed a small piece of code as part of a (supposedly) simple ticket and wound up failing scores of seemingly unrelated test cases, you have experienced the frustration of brittle tests. </p>
<p>Good tests should be updated less frequently, while bad tests sap productivity with needless changes. Measuring how often tests are changed can be an effective way of finding brittle tests. </p>
<h3 id="heading-flakiness-reliability">Flakiness (Reliability)</h3>
<p>A flaky test is a non-deterministic test. That is to say; sometimes it fails despite neither the code nor the test changing. When tests are flaky, our continuous integration and deployment pipelines suffer from unnecessary re-runs, our lead time for changes grows unnecessarily, and worst of all, engineers pay less attention to the tests.</p>
<p>We can measure flakiness in the number of times we can run a test suite and have it return the same result. That is to say that a test suite that, if I ran it twice, failed once and succeeded once, its flakiness would be 100%. The equation for flakiness percentage is <code>failures / successes</code></p>
<h3 id="heading-speed">Speed</h3>
<p>Tests work best when they can provide real-time feedback as part of an engineer's workflow; The best tests can run in the IDE while the engineer is coding. However, as we will see later, some tests must sacrifice speed to interrogate the subject under test.</p>
<p>Fast tests improve the local development experience, increase the utilisation of tests amongst engineering teams, and improve the lead time to deployment.</p>
<h3 id="heading-readability">Readability</h3>
<p>Ultimately our tests need to document expected behaviour for other engineers to read. Since the best tests are updated the least, they will likely be read many more times than they are written. The best tests are clear, concise, and simple. Each test should only test one behaviour. The prerequisites, action, and expected result should be clearly expressed such that even someone unfamiliar with the code can understand the test.</p>
<p>A good litmus test for test cases can be checking if a non-technical stakeholder such as the Product Manager understands them.</p>
<h3 id="heading-accuracy">Accuracy</h3>
<p>It's great having durable, reliable, fast, and readable tests, but it is all for naught if the tests continue passing when the system's behaviour changes in a breaking way. However, accuracy doesn't become a focus for many teams because they struggle with the other dimensions of testing. Most teams only write example-based tests, which have the least accuracy, but other methods such as property-based testing and mutation testing can help ensure that our tests are rigorous and improve their accuracy.</p>
<h1 id="heading-big-test-little-test">Big Test, Little Test</h1>
<p>While most people consider tests in terms of unit, integration, and end-to-end tests, Google has a more precise hierarchy of tests: Small, medium, and large. But what do these classifications mean? They might sound subjective, but they each have a specific objective definition.</p>
<h3 id="heading-small-tests">Small Tests</h3>
<p>Google's rules for small tests are:</p>
<ul>
<li>The test must run on one thread on one machine</li>
<li>The test must not use sleep</li>
<li>The test cannot perform I/O</li>
<li>The test cannot make blocking/async calls</li>
</ul>
<p>In TypeScript, these translate to:</p>
<ul>
<li>The test must run in one Node process</li>
<li>The test must run synchronously: it cannot use async/await/promises</li>
<li>It must run in a single tick of the event loop (this means no using setTimeout or setInterval for side-effects)</li>
<li>The test cannot perform I/O (it cannot use synchronous file-system access, for example)</li>
</ul>
<p>I would also add two more constraints:</p>
<ul>
<li>The test must not utilise system time</li>
<li>The test must not utilise pseudo-random number generators or randomness of any kind</li>
</ul>
<p>In practical terms, this rules out testing against a database, mocking asynchronous systems (a cause of much brittleness AND inaccuracy), and even starting an Express server in the test suite.</p>
<h3 id="heading-medium-tests">Medium Tests</h3>
<p>Google's rules for medium tests are:</p>
<ul>
<li>The test may run on multiple threads or processes, but only on one machine</li>
<li>The test may make blocking calls</li>
<li>The test may call localhost, but not the network</li>
</ul>
<p>In TypeScript, these translate to:</p>
<ul>
<li>The test may run an additional node process, may utilise a local database, etc</li>
<li>The test may use async/await (Promises)</li>
<li>The test may use multiple ticks of the event loop</li>
<li>The test may perform I/O</li>
<li>The test may call localhost, but not the network</li>
</ul>
<p>In practical terms, you can now test against a local database, use a tool such as Supertest to test your Express server, and more.</p>
<h3 id="heading-large-tests">Large Tests</h3>
<p>For large tests, all the constraints are removed. An example of a large test would be using Cypress to run end-to-end tests against a staging deployment. As we know, networks are both laggy and unreliable, so these tests have the highest flakiness and slowness.</p>
<h2 id="heading-comparing-test-sizes">Comparing Test Sizes</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651991855506/1dx1scS-B.png" alt="Test Sizes.excalidraw.png" class="image--center mx-auto" /></p>
<p>With these constraints in mind, we can see that small tests are the best performing in all categories:</p>
<ul>
<li><strong>Durability:</strong> Small tests necessitate a limited scope, so it will be less likely that they will inadvertently rely on related components</li>
<li><strong>Reliability:</strong> The constraints for small tests remove all opportunities for non-determinism to enter the test suite</li>
<li><strong>Speed:</strong> Small tests are CPU bound, consume the least resources and are therefore faster than their larger counterparts</li>
<li><strong>Readability:</strong> Small tests are generally simpler and thus easier to understand</li>
<li><strong>Accuracy:</strong> While small tests are not intrinsically more accurate on their own, their other attributes make it easier for engineers to write and run more of them, more examples, and even use approaches such as property-based testing.</li>
</ul>
<p>For this reason, we strive to define our test pyramid, not in terms of unit or integration tests, but instead based on this size taxonomy:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651988374831/WSbGyEjRV.png" alt="Test Size Pyramid.excalidraw.png" class="image--center mx-auto" /></p>
<p>As we move further up the pyramid, we lose out on all five factors of a good test. For this reason, we want to structure our code such that this distribution of test sizes is feasible. This code structure is where fDDD can help us!</p>
<h1 id="heading-testing-in-functional-ddd">Testing in Functional DDD</h1>
<p>Writing Small tests can be highly challenging unless we take care to structure our code in specific ways. Luckily <a target="_blank" href="https://antman-does-software.com/functional-domain-driven-design-simplified">Functional DDD</a> gives us precisely this structure. From here, I will continue assuming you are familiar with fDDD.</p>
<h3 id="heading-small-tests-derivers-andamp-invariants">Small Tests: Derivers &amp; Invariants</h3>
<p>Since both Derivers and Invariants are Pure Functions, their tests automatically meet the criteria for smallness. This functional purity allows us to test our most valuable code quickly, easily, and to a high standard.</p>
<h3 id="heading-medium-tests-controllers">Medium Tests: Controllers</h3>
<p>Controllers break the synchrony constraint and so cannot be deemed small tests. However, we can still benefit significantly by using the Partially Applied Controller pattern to reduce dependencies such as a local database, remove network requests, etc. We should strive to make even medium-sized tests as small as possible!</p>
<p>Another way of improving the balance between small and medium-sized tests is to remove tests for Controllers where they provide little value. Testing a Controller that simply flushes the derived result to the database on success is not a very valuable test. </p>
<p>If the test provides almost no value, then it may have a net-negative impact due to the increased brittleness and flakiness it could introduce. Having a solid foundation of small tests makes it easier to remove low-value medium-sized tests.</p>
<h3 id="heading-large-tests">Large Tests</h3>
<p>Large tests are not a concern of fDDD, but we will mention them for completeness. Large tests are most useful for testing configuration, emergent behaviour, and evolutionary architecture against fitness functions.</p>
<p>Types of large tests include:</p>
<ul>
<li>Automated UI tests on deployed applications</li>
<li>Automated API tests on deployed applications</li>
<li>Load testing on deployed applications</li>
<li>Performance testing on deployed applications</li>
</ul>
<p>The critical distinction here is that we are testing the entire system in the context of a deployed application. We could implement UI, API, visual regression, and performance testing as medium-sized tests, which would make them easy to run on pull-request, but they wouldn't be testing the system.</p>
<p>However, large tests are expensive, and most teams need to prioritise which non-functional requirements are critical to their applications.</p>
<h1 id="heading-improving-readability">Improving Readability</h1>
<p>This article has spoken about flakiness, brittleness, accuracy, and speed, but we haven't talked much about readability yet. Let's discuss a few simple ways we can improve the readability of our tests.</p>
<h3 id="heading-hermeticity">Hermeticity</h3>
<p>A test should contain everything it needs for setup and teardown while making no assumptions about the external environment, such as the state of the database. </p>
<p>For example, we once had a flaky test suite that was hard to track down. It was slightly more reliable in CI, but it was extremely flaky on local. It turned out that there were two pieces of code referencing a feature toggle in the database. One of these tests would change the state of the toggle, so the order that the tests were executed in could change the outcome of the test suite! But worse than that, people's local databases often weren't in a state compatible with the test.</p>
<p>In this case, the test's readability was poor because we defined part of the test's behaviour in an entirely separate system. A system that the test code didn't reference.</p>
<h3 id="heading-no-logic">No logic</h3>
<p>Tests should be so simple that they must not use control-flow statements such as if, for, and while. If your test is so complex it could have its own test, it isn't going to be effective, and it certainly isn't going to be clear to another developer when their change breaks your test.</p>
<h3 id="heading-behaviour-based-tests">Behaviour-based tests</h3>
<p>Each individual test case must test one and only one behaviour. This rule is made clearest with an example:</p>
<pre><code class="lang-ts">it(<span class="hljs-string">'updateUser'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> user: User = { email: <span class="hljs-string">'foo@bar.com'</span> };
  <span class="hljs-keyword">const</span> result1 = updateUser(user, { name: <span class="hljs-string">'bronson'</span> });
  expect(result1).to.deep.eq({ email: <span class="hljs-string">'foo@bar.com'</span>, name: <span class="hljs-string">'bronson'</span> });

  <span class="hljs-keyword">const</span> result2 = updateUser(user, { email: <span class="hljs-string">'bar@foo'</span> });
  expect(result2).to.deep.eq({ error: <span class="hljs-string">'invalid email address'</span> });
});
</code></pre>
<p>The test above is trying to test an entire function, rather than a single behaviour of that function. We can improve the clarity of this test by splitting it into two behaviour based tests:</p>
<pre><code class="lang-ts">describe(<span class="hljs-string">'updateUser'</span>, <span class="hljs-function">() =&gt;</span> {
  it(<span class="hljs-string">'should add a name property to an existing user'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> user: User = { email: <span class="hljs-string">'foo@bar.com'</span> };
    <span class="hljs-keyword">const</span> result = updateUser(user, { name: <span class="hljs-string">'bronson'</span> });
    expect(result).to.deep.eq({ email: <span class="hljs-string">'foo@bar.com'</span>, name: <span class="hljs-string">'bronson'</span> });
  });

  it(<span class="hljs-string">'should return an error when supplied an invalid email address'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> user: User = { email: <span class="hljs-string">'foo@bar.com'</span> };
    <span class="hljs-keyword">const</span> result = updateUser(user, { email: <span class="hljs-string">'bar@foo'</span> });
    expect(result).to.deep.eq({ error: <span class="hljs-string">'invalid email address'</span> });
  });
});
</code></pre>
<p>Now if I accidentally break the email validation logic, the test failure cause will be apparent even before I read the test code.</p>
<h3 id="heading-damp-not-dry">DAMP, not DRY</h3>
<p>Google defines DAMP as promoting "Descriptive And Meaningful Phrases" -- a reverse-engineered acronym if I ever saw one. However, the principle is sound; tests benefit more from clarity than code reuse.</p>
<p>In the following example, we have created helper functions to setup test state:</p>
<pre><code class="lang-ts">it(<span class="hljs-string">'should allow users to send friend request'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> users = createTestUsers(<span class="hljs-number">2</span>);
  <span class="hljs-keyword">const</span> { outcome } = sendFriendRequest({ <span class="hljs-keyword">from</span>: users[<span class="hljs-number">0</span>], to: users[<span class="hljs-number">1</span>] });
  expect(outcome).to.eq(<span class="hljs-string">'SUCCESS'</span>);
});

it(<span class="hljs-string">'should not allow a banned user to send friend request'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> users = createTestUsers(<span class="hljs-number">2</span>, <span class="hljs-literal">true</span>);
  <span class="hljs-keyword">const</span> { outcome } = sendFriendRequest({ <span class="hljs-keyword">from</span>: users[<span class="hljs-number">0</span>], to: users[<span class="hljs-number">1</span>] });
  expect(outcome).to.eq(<span class="hljs-string">'BANNED_USER_CANNOT_SEND_REQUEST'</span>);
  <span class="hljs-comment">// This test suite is failing</span>
});
</code></pre>
<p>And then elsewhere in the file:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> createTestUsers = (numUsers: <span class="hljs-built_in">number</span>, ...bannedUsers: <span class="hljs-built_in">boolean</span>[]): User[] =&gt; {
  <span class="hljs-keyword">const</span> users = <span class="hljs-built_in">Array</span>.from({ length: numUsers }, <span class="hljs-function">(<span class="hljs-params">_, idx</span>) =&gt;</span> ({
    email: <span class="hljs-string">`user<span class="hljs-subst">${idx+<span class="hljs-number">1</span>}</span>@test.co`</span>,
    banned: bannedUsers[idx+<span class="hljs-number">1</span>] ?? <span class="hljs-literal">false</span>,
  });
  <span class="hljs-keyword">return</span> users;
};
</code></pre>
<p>The issue is that while reading the test cases, it is unclear what is happening, and is actually dependent on iteration logic inside the helper. Did you spot the bug? Consider instead:</p>
<pre><code class="lang-ts">it(<span class="hljs-string">'should allow users to send friend request'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> user1: User = { email: <span class="hljs-string">'user1@test.co'</span> };
  <span class="hljs-keyword">const</span> user2: User = { email: <span class="hljs-string">'user2@test.co'</span> };

  <span class="hljs-keyword">const</span> { outcome } = sendFriendRequest({ <span class="hljs-keyword">from</span>: user1, to: user2 });

  expect(outcome).to.eq(<span class="hljs-string">'SUCCESS'</span>);
});

it(<span class="hljs-string">'should not allow a banned user to send friend request'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> user1: User = { email: <span class="hljs-string">'user1@test.co'</span>, banned: <span class="hljs-literal">true</span> };
  <span class="hljs-keyword">const</span> user2: User = { email: <span class="hljs-string">'user2@test.co'</span> };

  <span class="hljs-keyword">const</span> { outcome } = sendFriendRequest({ <span class="hljs-keyword">from</span>: user1, to: user2 });

  expect(outcome).to.eq(<span class="hljs-string">'BANNED_USER_CANNOT_SEND_REQUEST'</span>);
});
</code></pre>
<p>Here the difference between test cases has been made explicit within the test cases themselves.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Now that we've defined the five factors of a good test suite, Google's test size taxonomy, the benefits of fDDD in test quality, and a few tips for improving test readability, hopefully, you have a few ideas for improving your team's automated testing. Of course, there is <em>a lot</em> more that one could say about writing great tests, but I will leave that for another article.</p>
<p><strong>Summary:</strong></p>
<ul>
<li>You can measure the quality of a test suite in terms of brittleness, flakiness, speed, accuracy, and readability</li>
<li>Google break their tests down by a taxonomy of sizes:<ul>
<li>Small: Single machine, single-threaded, synchronous, with no I/O or sleep</li>
<li>Medium: Single machine, multi-threaded, asynchronous, localhost only</li>
<li>Large: No constraints</li>
</ul>
</li>
<li>fDDD provides a pattern for implementing a high number of small tests protecting the most valuable business rules<ul>
<li>Use small tests to test derivers and invariants</li>
<li>Use medium tests to test controllers</li>
<li>Use partially applied controllers to keep medium-sized tests as small as possible</li>
<li>Use large tests to test non-functional system factors such as scalability</li>
</ul>
</li>
<li>Readability can be improved by:<ul>
<li>Hermeticity: Remove environmental dependencies from tests</li>
<li>No logic or control flow in tests</li>
<li>Writing individual test cases to test an individual behaviour rather than functions</li>
<li>Writing explicit DAMP test cases where relevant information is repeated rather than abstracted</li>
</ul>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Functional Domain Driven Design: Simplified]]></title><description><![CDATA[Domain-Driven Design (DDD) simplifies the development and maintenance of complex software applications. However, two seminal books on the topic, Domain-Driven Design and Domain-Driven Design Distilled focus on an object-oriented implementation.
How c...]]></description><link>https://antman-does-software.com/functional-domain-driven-design-simplified</link><guid isPermaLink="true">https://antman-does-software.com/functional-domain-driven-design-simplified</guid><category><![CDATA[DDD]]></category><category><![CDATA[Functional Programming]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sun, 01 May 2022 13:45:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/F93PQmh4krI/upload/v1651466857798/mB2Tsb902.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Domain-Driven Design</strong> (<strong>DDD</strong>) simplifies the development and maintenance of complex software applications. However, two seminal books on the topic, <a target="_blank" href="https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215">Domain-Driven Design</a> and <a target="_blank" href="https://www.amazon.com/Domain-Driven-Design-Distilled-Vaughn-Vernon/dp/0134434420">Domain-Driven Design Distilled</a> focus on an object-oriented implementation.</p>
<p>How can we translate the tactical patterns of DDD into a functional programming paradigm? This article will show you how I do it in TypeScript and the many simplifications and benefits. Let’s begin with a refresher on DDD.</p>
<h1 id="heading-domain-driven-design">Domain Driven Design</h1>
<p>Domain-Driven Design focuses on business rules, business language, and business problems as the primary focus of software design. Instead of primarily designing software in architectural layers (e.g. database, data transfer objects, models, controllers, views), it focuses on building software with discrete domains or bounded contexts (e.g. billing, authentication, insurance sales, insurance claims, etc.)</p>
<p>DDD aligns our software architecture along the axis of most significant change; the business generates requests for changes to implement new business rules implemented in the domain. Focusing solely on software layers may produce greater code reuse, but reuse inhibits the ability to respond to changing business requirements. If both the Sales and Claims domain use an Insurance Policy entity, then a change on behalf of the sales team could inadvertently create a bug for the claims team. In DDD, we would make a separate SalesPolicy entity and a PolicyClaim entity, thus decoupling these two business domains in the codebase.</p>
<p>Domain-Driven Design splits its patterns into two categories: strategic patterns and tactical patterns. The strategic patterns are the easiest to reuse in any language, framework, and programming paradigm. They are universal aspects of software design.</p>
<p>However, the tactical patterns in DDD are tightly coupled to an object-oriented programming style and influenced by the capabilities of languages prevalent during DDD’s conception. These tactical patterns are where we will apply a functional approach to DDD and modify, remove, or create new tactical patterns.</p>
<p>Before we move into Functional implementations of tactical DDD patterns, let’s review the strategic patterns in DDD. Feel free to skip ahead if you are already familiar with these concepts.</p>
<h2 id="heading-strategic-ddd-patterns">Strategic DDD Patterns</h2>
<p>These patterns govern the overall approach to your code and even your system architecture. They are relevant regardless of the language, framework, or paradigm. Since there is already a wealth of information about these patterns, we will only briefly cover some of the most crucial strategic patterns.</p>
<h3 id="heading-bounded-context">Bounded Context</h3>
<p>A Bounded Context is a conceptual boundary in designing a system wherein the meaning of business terms is ubiquitous and consistent. For example, we may have the concept of a Policy entity in insurance. However, different teams in the business will have different interpretations of a policy, i.e. the sales team, the claims team, and the actuarial team will all assign the Policy entity different meanings.</p>
<p>By creating a bounded context in our design, we can effectively isolate each domain from the other. I use the term bounded context (context) and domain interchangeably throughout this article.</p>
<h3 id="heading-context-map">Context Map</h3>
<p>Context mapping is how we identify, understand, and communicate the Contexts/Domains in our system. You will often identify common entities in context mapping — an important tenet of DDD is that we do not attempt to remove or share common entities between contexts. Instead, we allow each context to maintain its implementation and version of the data within its domain.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651400184375/6LCxDJV48.png" alt="Bounded contexts.excalidraw.png" /></p>
<h3 id="heading-anti-corruption-layer">Anti-corruption Layer</h3>
<p>In the above diagram, the "claims context" would implement an anti-corruption layer to adapt the incoming policy data into the format and structure meaningful to the "claims context". This pattern prevents details of the upstream entity from leaking into the downstream context and adds some protection against upstream changes.</p>
<h1 id="heading-functional-shortcomings-in-oop-ddd">Functional shortcomings in OOP DDD</h1>
<p>Much of Classical DDD refers to methods of correctly allocating behaviour to the correct entity, aggregate root, domain service, or value object. Many of its rules aim to give DDD practitioners confidence in correctly distributing behaviour amongst various classes in their implementation.</p>
<p>However, <strong>Functional Domain Driven Design</strong> (<strong>fDDD</strong>) does not share these problems as data and behaviour are uni-directionally coupled rather than bi-directionally. That is to say that entities in Functional Programming cannot have their own behaviour but instead couples functions to data in the form of parameter types.</p>
<blockquote>
<p>Any number of functions can use entities, but functions can only use entities matching their type signature.</p>
</blockquote>
<p>Let's take stock of the Classical DDD concepts we have abandoned in fDDD:</p>
<ul>
<li><p>Aggregate Root: In fDDD, transactions are bound to the Controller</p>
</li>
<li><p>Value Object: In fDDD, values are just literal values</p>
</li>
<li><p>Service: In fDDD, this is most closely related to the Controller</p>
</li>
<li><p>Domain Service</p>
</li>
<li><p>Factories</p>
</li>
</ul>
<h1 id="heading-functional-ddd-tactical-patterns">Functional DDD Tactical Patterns</h1>
<p>It’s time to consider the practical implementation of DDD’s Tactical Patterns within a Functional Programming paradigm. We have two layers within these patterns: a Functional Core and an Imperative Shell.</p>
<p>The Functional Core implements tactical patterns using only Pure Functions. This layer is where we implement the bulk of our business rules since Pure Functions provide us with the greatest degree of predictability, reliability, testability, and changeability.</p>
<p>In the Imperative Shell, we want to use tactical patterns that help us coordinate between our systems and our business rules. For example, code in the imperative shell may be responsible for retrieving necessary data from the database, checking our invariants, inserting the changes back into the database, and dispatching an email.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651373454036/xk1qbf8mZ.png" alt="Functional DDD Patterns.excalidraw.png" /></p>
<p>Let’s start from the top with our Functional implementation of entities, which lives across both Imperative Shell and Functional Core.</p>
<h2 id="heading-entities">Entities</h2>
<p>In fDDD, you will commonly implement Domain Entities as type definitions. For example, I might define a Policy in the Sales domain as follows:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> Policy = {
  id: <span class="hljs-built_in">number</span>;
  customer: Customer;
  salesPerson: Staff;
  createdAt: <span class="hljs-built_in">Date</span>;
}
</code></pre>
<p>While this approach to defining the entity is straightforward, it alludes to our implementation of various adaptors either in an anti-corruption layer or in our repositories, as we will cover soon.</p>
<p>A useful example might be our approach to parsing this entity when we receive it across the network. In that case, we may implement a parser function such as:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> PolicyParser = <span class="hljs-function">(<span class="hljs-params">rawData: unknown</span>) =&gt;</span> Policy | ParseError;
</code></pre>
<p>This parsing function would create a type-safe run-time implementation for bringing the Policy entity into our domain layer. Rather than implementing these parsers by hand, I often use a run-time typing package like <a target="_blank" href="https://github.com/colinhacks/zod">zod</a>.</p>
<h2 id="heading-invariants-functional-core">Invariants: Functional Core</h2>
<p>Invariants are Pure Functions with only one job: check that the provided entity meets a given business rule. In the case of an address entity, we might write an invariant that confirms that the provided phone number has an area code matching the address region.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> validatePhoneMatchesCountry = (address: Address): <span class="hljs-function"><span class="hljs-params">boolean</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (address.phoneNumber ?? <span class="hljs-literal">false</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }
  <span class="hljs-keyword">return</span> getCountryFromAreacode(address.phoneNumber) === address.country;
}
</code></pre>
<p>As you can see, invariant functions can be tiny and very easy to test.</p>
<p>We primarily use Invariants by composing them inside Derivers. We may also, cautiously, create an invariant comprised of other Invariants if many Derivers share the exact composition of Invariants.</p>
<h2 id="heading-derivers-functional-core">Derivers: Functional Core</h2>
<p>Derivers are Pure Functions created to support a specific operation, such as <code>createAddress</code> or <code>cancelSubscription</code>. When we call a deriver, we must pass it all the information it requires to either derive the delta for our change or return an outcome indicating the cause for failure.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> UpgradeSubscriptionOutcome = UpgradeSucceeded 
  | AccountOverdrawn 
  | InvalidSubscriptionStatus;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> deriveUpgradeSubscriptionOutcome = (
  newPlanLevel: Subscription[<span class="hljs-string">'planLevel'</span>],
  subscription: Subscription, 
  customer: Customer,
  upgradePriceMap: PriceMap
): <span class="hljs-function"><span class="hljs-params">UpgradeSubscriptionOutcome</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (!validateCustomerBalance(customer)) {
    <span class="hljs-keyword">return</span> {
      outcome: <span class="hljs-string">'ACCOUNT_OVERDRAWN'</span>,
      payload: { balanceOwing: customer.balance },
    };
  }
  <span class="hljs-keyword">if</span> (subscription.status !== <span class="hljs-string">'ACTIVE'</span>) {
    <span class="hljs-keyword">return</span> {
      outcome: <span class="hljs-string">'INVALID_SUBSCRIPTION_STATUS'</span>,
      payload: { 
        currentStatus: subscription.status, 
        expectedStatus: <span class="hljs-string">'ACTIVE'</span> 
      },
    };
  }
  <span class="hljs-keyword">const</span> prorataDays = calculateProrata(subscription);
  <span class="hljs-keyword">const</span> upgradeFee = upgradePriceMap[subscription.planLevel][newPlanLevel];
  <span class="hljs-keyword">return</span> {
    outcome: <span class="hljs-string">'SUCCEEDED'</span>,
    payload: {
      prorataDays,
      upgradeFee,
    },
  };
}
</code></pre>
<p>In the Deriver above, we check two potential failure cases and then calculate the change required by the requested upgrade. Consider what we are not doing in this function:</p>
<ul>
<li><p>Not getting the customer's details from the database</p>
</li>
<li><p>Not sending the customer a receipt via email</p>
</li>
<li><p>Not calling a payment provider to charge their credit card</p>
</li>
<li><p>Not saving the changes to the database</p>
</li>
</ul>
<p>The core business rules for this operation have been condensed into a single function, enabling us to test every business rule. By keeping the scope of this function narrow, we can maintain its functional purity. This limited scope makes writing unit tests for this function less complex and makes the test itself more reliable. There are no opportunities for non-determinism, no database fixtures, and no stubs or mocks.</p>
<h2 id="heading-controllers-imperative-shell">Controllers: Imperative Shell</h2>
<p>We use Controllers to coordinate asynchronous parts of the system during an operation and call the Deriver. In the above example, the Controller would be responsible for each step we identified as out of scope for the Deriver.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> upgradeSubscription = <span class="hljs-keyword">async</span> (
  customerId: Customer[<span class="hljs-string">'id'</span>], 
  newPlanLevel: Subscription[<span class="hljs-string">'planLevel'</span>]
): <span class="hljs-built_in">Promise</span>&lt;UpgradeSubscriptionOutcome&gt; =&gt; {
  <span class="hljs-keyword">const</span> [customer, subscription] = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all([
    customerRepo.getById(customerId),
    subscriptionRepo.getByCustomerId(customerId),
  ]);

  <span class="hljs-keyword">const</span> { outcome, payload } = deriveUpgradeSubscriptionOutcome(
    newPlanLevel,
    subscription,
    customer,
    UPGRADE_PRICE_MAP,
  );

  <span class="hljs-keyword">switch</span> (outcome) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">'INVALID_SUBSCRIPTION_STATUS'</span>: {
      <span class="hljs-keyword">return</span> { outcome, payload };
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">'ACCOUNT_OVERDRAWN'</span>: {
      <span class="hljs-keyword">await</span> sendRepaymentReminder(customer, payload.balanceOwing);
      <span class="hljs-keyword">return</span> { outcome, payload };
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">'SUCCEEDED'</span>: {
      <span class="hljs-keyword">const</span> updatedSubscription = <span class="hljs-keyword">await</span> subscriptionRepo.update({ 
        ...subscription,
        prorataDays: payload.prorataDays,
        planLevel: newPlanLevel,
      });
      <span class="hljs-keyword">const</span> transactionId = <span class="hljs-keyword">await</span> chargeCreditCard(customer.defaultCard);
      <span class="hljs-keyword">await</span> sendInvoice(
        customer, 
        payload.upgradeFee, 
        updatedSubscription,
        transactionId
      );
      <span class="hljs-keyword">return</span> {
        outcome,
        payload: {
          ...payload,
          transactionId,
        }
      }
    }
    <span class="hljs-keyword">default</span>: {
      isNever(outcome);
      <span class="hljs-keyword">break</span>;
    }
  }
}
</code></pre>
<p>As you can see in the above function, we are now dealing primarily with the asynchronous parts of the system. In these controller functions, we want as little business logic as possible. The example above is particularly complex; often, controllers only retrieve data from the database and save on success.</p>
<p>Notice, however, that the Controller does not make any business decisions. It only takes actions based on the result of the Deriver. It does not validate the subscriptions, and it does not check any rules.</p>
<p>However, the actions we take in certain circumstances, such as sending an email or charging a credit card, could be something we want to test. In that case, we can use the Partially Applied Controller pattern.</p>
<h3 id="heading-testing-via-partially-applied-controllers">Testing via Partially Applied Controllers</h3>
<p>If we want to make the previous example more testable, we can similarly use a partial function as we might use dependency injection in Object-Oriented programming. Let's take a look at an example:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> createUpgradeSubscriptionController = (
  customerRepo: CustomerRepository,
  subscriptionRepo: SubscriptionRepository,
  sendRepaymentReminder: <span class="hljs-function">(<span class="hljs-params">customer: Customer, balanceOwing: <span class="hljs-built_in">number</span></span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt;,
  chargeCreditCard: <span class="hljs-function">(<span class="hljs-params">card: CreditCard</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;Transaction[<span class="hljs-string">'id'</span>]&gt;,
  sendInvoice: InvoiceSender,
) =&gt; <span class="hljs-keyword">async</span> (
  customerId: Customer[<span class="hljs-string">'id'</span>], 
  newPlanLevel: Subscription[<span class="hljs-string">'planLevel'</span>]
): <span class="hljs-built_in">Promise</span>&lt;UpgradeSubscriptionOutcome&gt; =&gt; {
  <span class="hljs-comment">/* Remaining implementation is identical to the previous example */</span>
};

<span class="hljs-comment">// In an adjacent test file</span>
<span class="hljs-keyword">const</span> fakeSendRepaymentReminder = sinon.fake.resolves();
<span class="hljs-keyword">const</span> fakeChargeCreditCard = sinon.fake.resolves(<span class="hljs-string">'1a2bc'</span>);
<span class="hljs-keyword">const</span> fakeSendInvoice = sinon.fake.resolves();
<span class="hljs-keyword">const</span> upgradeSubscriptionTestController = createUpgradeSubscriptionController(
  customerRepoInMemory,
  subscriptionRepoInMemory,
  fakeSendRepaymentReminder,
  fakeChargeCreditCard,
  fakeSendInvoice,
);
</code></pre>
<p>Now I can supply a test implementation of <code>customerRepo</code> &amp; <code>subscriptionRepo</code> while also providing fakes for the other functions. Since this is a much more complex test, we don't want to repeat ourselves testing the Deriver's business rules. Instead, we only want to assert what functions the Controller calls for each of the Deriver's three possible scenarios.</p>
<h2 id="heading-repositories-imperative-shell">Repositories: Imperative Shell</h2>
<p>Repository patterns for retrieving entities on a CRUD basis are not solely a DDD pattern. Nor is it a significantly changed pattern between Functional and OO paradigms. For the sake of fDDD, I will only mention a few constraints to keep in mind for your repositories:</p>
<ul>
<li><p>Keep database concerns constrained entirely to your repository layer</p>
</li>
<li><p>Parse, transform, or map all data types going into and out of your repository layer</p>
</li>
</ul>
<p>A generic repository type could look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> Repository&lt;T&gt; = {
   getMatching: <span class="hljs-function">(<span class="hljs-params">query: Query&lt;T&gt;</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;T[]&gt;;
   getById: <span class="hljs-function">(<span class="hljs-params">id: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;T | <span class="hljs-literal">undefined</span>&gt;;
   create: <span class="hljs-function">(<span class="hljs-params">item: Omit&lt;T, <span class="hljs-string">'id'</span>&gt;</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;T&gt;;
   update: <span class="hljs-function">(<span class="hljs-params">item: T</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;T | <span class="hljs-literal">undefined</span>&gt;;
   upsert: <span class="hljs-function">(<span class="hljs-params">item: T</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;T&gt;;
   <span class="hljs-keyword">delete</span>: <span class="hljs-function">(<span class="hljs-params">id: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-literal">undefined</span> | MissingEntityError&gt;;
 };

<span class="hljs-keyword">type</span> Query&lt;T&gt; = {
  [k: keyof T]: { operator: Operator, value: unknown },
}
</code></pre>
<p>Where the implementation of a <code>create</code> function may look like:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> subscriptionRepo: Repository&lt;Subscription&gt; = {
  create: <span class="hljs-keyword">async</span> (subscription) =&gt; {
    <span class="hljs-keyword">const</span> objKeys = <span class="hljs-built_in">Object</span>.keys(subscription);
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> pool.query&lt;T&gt;(
      <span class="hljs-string">`
      INSERT INTO "subscriptions"
      (`</span>${objKeys.join(<span class="hljs-string">','</span>)}<span class="hljs-string">`)
      VALUES (`</span>${objKeys.map(<span class="hljs-function">(<span class="hljs-params">_, i</span>) =&gt;</span> <span class="hljs-string">`$<span class="hljs-subst">${i+<span class="hljs-number">1</span>}</span>`</span>).join(<span class="hljs-string">','</span>)}<span class="hljs-string">`)
      RETURNING *
      `</span>,
      <span class="hljs-built_in">Object</span>.values(subscription)
    );
    <span class="hljs-keyword">return</span> result.rows.map(parseSubscriptionFromDb)[<span class="hljs-number">0</span>];
  },
  <span class="hljs-comment">// remaining repo functions omitted</span>
};
</code></pre>
<p>In the above example, our repository maps from the database type back to the domain entity types using the <code>parseSubscriptionFromDb</code> function. This mapping helps us maintain a layer of separation between the database and our implementation. It also allows us to narrow the types further while throwing errors if the database returns unexpected data.</p>
<h1 id="heading-software-architectural-context">Software Architectural Context</h1>
<p>Now that we have defined the patterns of our fDDD implementation, we need to consider its place in our overall software architecture. If we were to implement fDDD in a typical Express.js application, we could implement it as follows:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651386094852/uBx1zyb6u.png" alt="FDDD Application software Architecture.excalidraw.png" /></p>
<p>Our Route Handler for a POST request to <code>/api/subscriptions</code> may look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handleSubscriptionPost: RequestHandler = <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> customerId = req.jwt.customerId;
  <span class="hljs-keyword">const</span> { subscriptionLevel, paymentToken } = req.body;

  <span class="hljs-keyword">const</span> { outcome, payload } = <span class="hljs-keyword">await</span> createSubscription(
    customerId, 
    subscriptionLevel, 
    paymentToken
  );

  <span class="hljs-keyword">switch</span> (outcome) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">'PAYMENT_FAILED'</span>: {
      res.status(<span class="hljs-number">400</span>);
      <span class="hljs-keyword">break</span>;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">'SUCCEEDED'</span>: {
      res.status(<span class="hljs-number">201</span>);
      <span class="hljs-keyword">break</span>;
    }
  }
  res.json({ outcome, payload });
};
</code></pre>
<p>All our route handler is responsible for is:</p>
<ul>
<li><p>Preparing data from HTTP requests for consumption by the Controller</p>
</li>
<li><p>Mapping outcomes back to HTTP status codes</p>
</li>
</ul>
<p>The route handler has no opinions on the business rules, and the domain code has no knowledge of content types or status codes.</p>
<p>When it comes to planning the structure of our codebase, there are primarily two dimensions we could choose as the basis for our files and folders:</p>
<ul>
<li><p>Application layers, e.g. database, route handlers, domain, services</p>
</li>
<li><p>Operations, e.g. <code>createSubscription</code>, <code>updateUser</code>, etc</p>
</li>
</ul>
<p>The former has the advantage that all of the code for a particular area is easy to find, but the trade-off is that implementing a change requires opening many folders. The latter has the advantage that all related code for any given change is likely to be nearby, but it is harder to find other code that may operate similarly or be affected by your change.</p>
<p>When we structure our codebase for a <a target="_blank" href="https://antman-does-software.com/your-microservices-are-slowing-you-down-could-domain-services-boost-productivity">Domain Service</a>, our preference is to keep all of the domain code as closely co-located as possible.</p>
<p>This preference means our top-level folder structure will be along the lines of:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651400579888/8Tq-20TcL.png" alt="Top level folder structure (wide).excalidraw-2.png" /></p>
<p>However, we will then split it into "entities" and "operations" sub-folders inside the domain folder.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651400806036/4FwZOSPBr.png" alt="Domain folder structure (wide).excalidraw.png" /></p>
<p>This structure increases the likelihood of reusing an entity's invariants in different Derivers for each operation.</p>
<h1 id="heading-wrapping-up">Wrapping Up</h1>
<p>Hopefully, this article has shown you how to implement Domain Driven Design within a Functional Programming paradigm and successfully make complex software more manageable. As you can see, fDDD simplifies many of the complicating factors in a classical DDD implementation. Here's a quick refresher on what we have learned:</p>
<ul>
<li><p>Entities are at minimum a type definition and may include a parser for run-time validation</p>
</li>
<li><p>Invariants take an Entity as an input parameter, validate it against a simple business rule, and return a boolean</p>
</li>
<li><p>Derivers take one or more Entities as an input parameter, validate it against operation-specific business rules, compose related Invariants, and return a discriminated union of potential outcomes</p>
</li>
<li><p>Controllers coordinate asynchronous parts of the system, including databases, on behalf of Derivers</p>
</li>
<li><p>The wider application accesses the Domain Layer strictly via Controllers</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Are you using map, forEach, and reduce wrong?]]></title><description><![CDATA[In JavaScript, Array.prototype.map, Array.prototype.forEach, and Array.prototype.reduce are used heavily in functional-style programming. However, I meet many developers missing a clear mental model of when or how to use each function.
This is a prob...]]></description><link>https://antman-does-software.com/are-you-using-map-foreach-and-reduce-wrong</link><guid isPermaLink="true">https://antman-does-software.com/are-you-using-map-foreach-and-reduce-wrong</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Functional Programming]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sun, 20 Mar 2022 09:34:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/wQLAGv4_OYs/upload/v1647767935709/bbKmCuU7c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In JavaScript, <code>Array.prototype.map</code>, <code>Array.prototype.forEach</code>, and <code>Array.prototype.reduce</code> are used heavily in functional-style programming. However, I meet many developers missing a clear mental model of when or how to use each function.</p>
<p>This is a problem because we use these functions not just for their behaviour but to communicate our code's intent. These functions reveal crucial details about the author's mental model of the code, the problem, and the solution.</p>
<p>In this article, I will attempt to couple a technical understanding of these functions to a semantic understanding in your mind. As a result, you should have a mental model of when to use these functions and why, plus what it means when you read code using them.</p>
<h2 id="heading-arrayprototypemap">Array.prototype.map</h2>
<p>Let's start with a simplified version of the official TypeScript definition for map and break it down. I have trimmed the type parameters down to the simplest and most common use case:</p>
<pre><code class="lang-ts">map&lt;U&gt;(
  callbackfn: <span class="hljs-function">(<span class="hljs-params">value: T</span>) =&gt;</span> U
) =&gt; U[];
</code></pre>
<p>Above, we have a <code>map</code> function that accepts a type argument <code>U</code>, a callback function, and returns an array (<code>U[]</code>). </p>
<p>The callback function takes a value of type <code>T</code> and returns a <code>U</code>. So it accepts a function that will take a value and transform it into another value.</p>
<p>Map calls this transformation function, the callback, for every element (<code>T</code>) in the array. The callback function transforms <code>T</code> into <code>U</code>, which <code>map</code> uses to transform each element, returning a new array of <code>U[]</code>. </p>
<p>Now I have lied to you slightly in the above explanation. Transforming implies a mutative operation. However, <code>map</code> does not and should not mutate anything. Instead, it returns an entirely new array (<code>U[]</code>) where every element has a 1 to 1 relationship with each element of <code>T[]</code>. We call the function <code>map</code> because it <em>maps</em> one type onto another. However, thinking of it as a transformation helps us think about when to use it.</p>
<blockquote>
<p>Use <code>.map</code> to "transform" an array of one thing into an array of something else.</p>
</blockquote>
<p>I will rewrite the above type definition again in a slightly different format, using intention revealing names for the type parameters. Then we will look at some real-world examples.</p>
<pre><code class="lang-ts"><span class="hljs-built_in">Array</span>&lt;InputType&gt;.map&lt;OutputType&gt;(
  callbackfn: <span class="hljs-function">(<span class="hljs-params">value: InputType</span>) =&gt;</span> OutputType
) =&gt; <span class="hljs-built_in">Array</span>&lt;OutputType&gt;;
</code></pre>
<p>This is an identical type definition to the first one I showed you. I've included it because I know that seeing it like this will help some people grok it.</p>
<p>In the next scenario, we have an array of <code>Person</code> objects, and we want to map these into an array of <code>ReactNodes</code>:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> Person = {
  firstName: <span class="hljs-built_in">string</span>;
  lastName: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
};

<span class="hljs-keyword">type</span> Props = { people: Person[] };

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> PeopleList: React.FC&lt;Props&gt; = <span class="hljs-function">(<span class="hljs-params">{people}</span>) =&gt;</span> (
  &lt;ul&gt;
    {people.map(
      <span class="hljs-function">(<span class="hljs-params">person</span>) =&gt;</span> (
        &lt;li&gt;{person.firstName} {person.lastName} | {person.age}&lt;/li&gt;
      )
    )}
  &lt;/ul&gt;
);
</code></pre>
<p>In this example, our map function transforms each <code>Person</code> object from the <code>people</code> array into a list item ReactNode. It's a simple use case and tightly coupled to the context we are using it -- the anonymous function we define for the map function isn't beneficial outside of the <code>PeopleList</code> component since it always returns a <code>&lt;li/&gt;</code> node.</p>
<p>Let's consider a use case where we might reuse our callback function in other contexts. In our next example, we are going to map an array of <code>People</code> into an array of full names:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> Person = {
  firstName: <span class="hljs-built_in">string</span>;
  lastName: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getFullnameFromPerson</span>(<span class="hljs-params">person: Person</span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${person.firstName}</span> <span class="hljs-subst">${person.lastName}</span>`</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">listPeople</span>(<span class="hljs-params">people: Person</span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">return</span> people.map(getFullnameFromPerson).join(<span class="hljs-string">', '</span>);
}
</code></pre>
<p>In the above example <code>listPeople</code> passes the <code>getFullnameFromPerson</code> function to <code>map</code>, but we could conceivably use <code>getFullnameFromPerson</code> in other contexts too.</p>
<p>Let's try a more complicated example now, where we want to create a layer between our database implementation and our TypeScript implementation. There are several things <code>map</code> can help us with:</p>
<ul>
<li>In Postgres, we use <code>snake_case</code> column names, but in TypeScript and JavaScript, we use <code>camelCase</code>. We'll want to map between these types</li>
<li>Enum types often come from another table; we'll map these too</li>
<li>We'll also map our <a target="_blank" href="https://github.com/brianc/node-pg-types/issues/78">64bit integer ids from strings</a> to <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt">BigInt</a></li>
</ul>
<p><strong>Javascript Types:</strong></p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> ArticleType = <span class="hljs-string">'GENERAL'</span> | <span class="hljs-string">'REVIEW'</span> | <span class="hljs-string">'EDITORIAL'</span>;

<span class="hljs-keyword">type</span> Article = {
  id: BigInt;
  articleType: ArticleType;
  author: <span class="hljs-built_in">string</span>;
  title: <span class="hljs-built_in">string</span>;
  urlSlug: <span class="hljs-built_in">string</span>;
  content: <span class="hljs-built_in">string</span>;
  publishedAt: <span class="hljs-built_in">Date</span>;
}
</code></pre>
<p><strong>Database Types:</strong></p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> ArticleRow = {
  id: <span class="hljs-built_in">string</span>; <span class="hljs-comment">// Bigints come back from pg-node as a string</span>
  article_type_id: <span class="hljs-built_in">number</span>;
  author: <span class="hljs-built_in">string</span>;
  title: <span class="hljs-built_in">string</span>;
  url_slug: <span class="hljs-built_in">string</span>;
  content: <span class="hljs-built_in">string</span>;
  published_at: <span class="hljs-built_in">Date</span>;
}

<span class="hljs-keyword">type</span> ArticleTypeRow = {
  id: <span class="hljs-built_in">number</span>; <span class="hljs-comment">// small int</span>
  <span class="hljs-keyword">type</span>: <span class="hljs-built_in">string</span>;
}
</code></pre>
<p><strong>Mapping:</strong></p>
<p>Now we have our cast of types, let's consider how we might map from these rather rough database types to these very usable javascript types. I'll write a function that selects the ten most recent articles, and we will use a series of maps to transform the query results.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> getRecentArticles = <span class="hljs-keyword">async</span> (): <span class="hljs-built_in">Promise</span>&lt;Article[]&gt; =&gt; {
  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> pool.query&lt;ArticleRow&gt;(<span class="hljs-string">`
    SELECT * FROM articles ORDER BY published_at LIMIT 10;
  `</span>);
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.all(result.rows.map(getArticleFromArticleRow));
}

<span class="hljs-keyword">const</span> getArticleFromArticleRow = <span class="hljs-keyword">async</span> (row: ArticleRow): <span class="hljs-built_in">Promise</span>&lt;Article&gt; =&gt; ({
  id: BigInt(row.id),
  articleType: <span class="hljs-keyword">await</span> getArticleTypeById(row.article_type_id),
  author: row.author,
  title: row.title,
  urlSlug: row.url_slug,
  content: row.content,
  publishedAt: row.published_at,
});

<span class="hljs-comment">/**
  This function memoises lookups to article type.
*/</span>
<span class="hljs-keyword">const</span> getArticleTypeById = (<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">let</span> cache: <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">string</span>, ArticleType&gt;;
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">async</span> (id: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">Promise</span>&lt;ArticleType&gt; =&gt; {
    <span class="hljs-keyword">if</span> (!cache) {
      <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> pool.query&lt;ArticleTypeRow&gt;(<span class="hljs-string">`
        SELECT * FROM article_types
      `</span>);
      cache = result.rows.reduce(
        <span class="hljs-function">(<span class="hljs-params">current, previous</span>) =&gt;</span> previous.set(current.id, current.type),
        <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>()
        <span class="hljs-comment">// This gives us an object where we can lookup article types by id</span>
      );
    }
    <span class="hljs-keyword">return</span> cache.get(id);
})();
</code></pre>
<p>The first function in the above example, <code>getRecentArticles</code>, simply returns the ten most recently published articles. </p>
<p>If you're wondering why we use <code>Promise.all</code> in the return statement, it is because our mapping callback function <code>getArticleFromArticleRow</code> returns a Promise. That means that the result of <code>result.rows.map(getArticleFromArticleRow)</code> is an array of promises. Still, the caller of <code>getRecentArticles</code> expects a single Promise containing an array of Articles.</p>
<p><code>Promise.all</code> takes an array of promises and maps them to an array of resolved values within a single promise, thus fulfilling our contract to the caller of <code>getRecentArticles</code>.</p>
<p>Why did we need <code>getArticleFromArticleRow</code> to be asynchronous (return a promise)? Because we look up the article type from the article type enum table in Postgres. You are correct in thinking that a Postgres query for every row in <code>getRecentArticles</code> is wasteful and slow. That is why <code>getArticleTypeById</code> actually caches the value locally.</p>
<p>Looking at the implementation, we use an immediately invoked function expression to create a <code>cache</code> value protected in the closure of the function definition for <code>getArticleTypeById</code>. This closure means no other part of the application can access <code>cache</code>, but all calls to <code>getArticleTypeById</code> utilise the same cache. It is effectively a functional singleton, providing a memoised lookup, so we only have to query the database for <code>ArticleTypes</code> once per node process.</p>
<p>As you can see, <code>Array.prototype.map</code> is a powerfully simple concept. You can build complex transformations with it, create asynchronous processes, build recursive solutions, and even chunk and distribute a map across multiple processes because it is monadic. </p>
<p>Now that we understand <code>map</code> let's see why <code>forEach</code> is different.</p>
<h2 id="heading-arrayprototypeforeach">Array.prototype.forEach</h2>
<p>Let's start with a simplified TypeScript definition of <code>forEach</code> like we did with <code>map</code>:</p>
<pre><code class="lang-ts">forEach(
  callbackfn: <span class="hljs-function">(<span class="hljs-params">value: T</span>) =&gt;</span> <span class="hljs-built_in">void</span>
): <span class="hljs-built_in">void</span>;
</code></pre>
<p>This looks pretty similar to <code>map</code>, except that the callback function doesn't return anything, and neither does <code>forEach</code> itself. Why might we want a function that returns nothing for each element of an array? Because we want to create <em>side effects</em>.</p>
<blockquote>
<p>Use <code>forEach</code> for creating side-effects beyond the local scope</p>
</blockquote>
<p>Let's understand side effects quickly by thinking about its opposite -- a pure function. Pure Functions always return the same value given the same inputs. The synchronous map functions in the previous example were pure, but the asynchronous functions accessing a database were impure because the return value could change between calls.</p>
<p>A function can also have a side effect when it affects part of the system beyond its input or output values. Let's move on to a concrete example.</p>
<p>If we wanted to send an email to every user in an array of users, we would use the <code>forEach</code> function if we do not care about the aggregate outcome of the email sending function. I'll give you an example, where our <code>handleMeetingBooked</code> function will email all attendees:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> Meeting = {
  name: <span class="hljs-built_in">string</span>;
  attendees: User[];
  date: <span class="hljs-built_in">Date</span>;
}

<span class="hljs-keyword">type</span> User = {
  email: <span class="hljs-built_in">string</span>;
  name: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">const</span> handleMeetingBooked = (meeting: Meeting): <span class="hljs-function"><span class="hljs-params">void</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> { attendees } = meeting;
  attendees.forEach(<span class="hljs-function">(<span class="hljs-params">attendee</span>) =&gt;</span> {
    sendMeetingEmail(attendee, meeting);
  });
}

<span class="hljs-keyword">const</span> sendMeetingEmail = (attendee: User, meeting: Meeting): <span class="hljs-function"><span class="hljs-params">void</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> emailBody = <span class="hljs-string">`Hi <span class="hljs-subst">${attendee.name}</span>,
    you have been invited to <span class="hljs-subst">${meeting.name}</span> 
    on <span class="hljs-subst">${meeting.date}</span> 
    with <span class="hljs-subst">${meeting.attendees.map(({name}</span>) =&gt; name).join(', ')}`</span>;
    sendEmail({ to: attendee.email, body: emailBody });
}
</code></pre>
<p>Notice how no part of <code>handleMeetingBooked</code> cares about what happens inside the callback function provided to <code>attendees.forEach</code>. We choose <code>forEach</code> instead of <code>map</code> because the behaviour is different and because it tells the readers of our code that the application shouldn't care about or depend upon the outcome of this email sending function.</p>
<p>Could I implement identical behaviour with <code>map</code>? Absolutely. Should I? <em>Absolutely not.</em> </p>
<p>Let's look at a typical <code>forEach</code> code smell -- mutating an external collection from within the callback provided to <code>forEach</code>.</p>
<pre><code class="lang-ts"><span class="hljs-comment">// BAD CODE, DO NOT DO THIS</span>
<span class="hljs-keyword">const</span> getUsersNames = (users: User[]): <span class="hljs-built_in">string</span>[] =&gt; {
  <span class="hljs-keyword">const</span> names = [];
  users.forEach(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> {
    names.push(user.name);
  });
  <span class="hljs-keyword">return</span> names;
}
</code></pre>
<p>What's wrong with the above code? Aside from a few unnecessary lines, it lies to the reader. It tells the person reading this code that the callback function won't change anything within the function, and then it mutates one of the function's variables! It mightn't seem so bad in a small function like this, but this is a recipe for bugs in larger functions.</p>
<blockquote>
<p><strong>Code smell:</strong> Using <code>.push</code> inside <code>forEach</code>. Consider alternative e.g. <code>.map</code> or <code>.filter</code></p>
</blockquote>
<p>The correct implementation of the above would simply be:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> getUsersNames = (users: User[]): <span class="hljs-built_in">string</span>[] =&gt; 
  users.map(<span class="hljs-function">(<span class="hljs-params">{name}</span>) =&gt;</span> name);
</code></pre>
<p>Now that we have understood the difference between <code>map</code> and <code>forEach</code>, it is time to consider the role <code>reduce</code> plays in our code.</p>
<h2 id="heading-arrayprototypereduce">Array.prototype.reduce</h2>
<p>Reduce is the array function that seems to cause the most confusion. Rather than starting with the type definition, I will offer you my mental model of what reduce is for, and then we can look at the code and see how it supports this way of thinking.</p>
<blockquote>
<p>Use <code>reduce</code> to reduce a collection of elements to a single aggregate entity.</p>
</blockquote>
<p>That is to say; I use reduce when I have many (an array) and want one (anything <em>but</em> an array). A typical example of this is summing the numbers in an array.</p>
<p>Let's take a look at a simplified version of the official type definition as we did before, this time keeping the goal of reducing down to a single aggregate entity in mind:</p>
<pre><code class="lang-ts">reduce&lt;U&gt;(
  callbackfn: <span class="hljs-function">(<span class="hljs-params">previousValue: U, currentValue: T</span>) =&gt;</span> U, 
  initialValue: U
): U;
</code></pre>
<p>The first thing to notice is that <code>reduce</code> returns a single <code>U</code> where <code>map</code> returned <code>U[]</code>. We can also see that we can supply an <code>initialValue</code>, but the type of this value must match the return type of the reduce function and that the callback function must also return this same type. </p>
<p>I'll write this again with intention revealing names:</p>
<pre><code class="lang-ts"><span class="hljs-built_in">Array</span>&lt;InputType&gt;.reduce&lt;OutputType&gt;(
  callbackfn: <span class="hljs-function">(<span class="hljs-params">previousValue: OutputType, currentValue: InputType</span>) =&gt;</span> OutputType, 
  initialValue: OutputType
): OutputType;
</code></pre>
<p>We'll reconsider the summation example, equipped with our new mental model and expanded type definition fresh in our minds:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> total = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>].reduce(
  <span class="hljs-function">(<span class="hljs-params">previous, current</span>) =&gt;</span> previous + current,
  <span class="hljs-number">0</span>
);
<span class="hljs-built_in">console</span>.log(total); <span class="hljs-comment">// 15</span>
</code></pre>
<p>Above, we started with a collection of numbers and wound up with just one number. We <em>reduced</em> it down to its aggregate: the variable <code>total</code> with a value of <code>15</code>.</p>
<p>How else could we use this? Perhaps we want to reduce an array of users down to a count of common names:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> User = {
  firstName: <span class="hljs-built_in">string</span>;
  lastName: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">type</span> CommonNamesAggregate = Record&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">number</span>&gt;;

<span class="hljs-keyword">const</span> countCommonNames = (users: User[]): <span class="hljs-function"><span class="hljs-params">CommonNamesAggregate</span> =&gt;</span> {
  <span class="hljs-keyword">return</span> users.reduce&lt;CommonNamesAggregate&gt;(
    <span class="hljs-function">(<span class="hljs-params">aggregate, user</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> currentCount = aggregate[user.firstName];
      <span class="hljs-keyword">if</span> (currentCount) {
        aggregate[user.firstName]++;
      } <span class="hljs-keyword">else</span> {
        aggregate[user.firstName] = <span class="hljs-number">1</span>;
      }
      <span class="hljs-keyword">return</span> aggregate;
    },
    {} <span class="hljs-comment">// We create our new CommonNamesAggregate here</span>
  );
</code></pre>
<p>In this case, our new aggregate entity is an object where each key is a first name from <code>users</code>, and the value is a count of the frequency of that name in the <code>users</code> array.</p>
<p>Let's write another flavour of the same <code>countCommonNames</code> solution and see if it helps us grok <code>reduce</code>:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> countCommonNames = (users: User[]): <span class="hljs-function"><span class="hljs-params">CommonNamesAggregate</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> uniqueNames = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>(users.map(<span class="hljs-function">(<span class="hljs-params">{firstName}</span>) =&gt;</span> firstName));
  <span class="hljs-keyword">const</span> commonNamesAggregate = [...uniqueNames].reduce(
    <span class="hljs-function">(<span class="hljs-params">obj, name</span>) =&gt;</span> {
      obj[name] = <span class="hljs-number">0</span>;
      <span class="hljs-keyword">return</span> obj;
    },
    {}
  );

  <span class="hljs-keyword">return</span> users.reduce&lt;CommonNamesAggregate&gt;(
    <span class="hljs-function">(<span class="hljs-params">aggregate, user</span>) =&gt;</span> {
      aggregate[user.firstName]++;
      <span class="hljs-keyword">return</span> aggregate;
    },
    commonNamesAggregate
  );
</code></pre>
<p>In this implementation, we first create the aggregate object separately and initialise its values to <code>0</code> by reducing our set of unique names, saving us from checking if the name already exists in our final reducer.</p>
<p>Now let's revisit <code>forEach</code> regarding the above code example. I'll implement the same behaviour using forEach, and then explain why it is a code smell.</p>
<pre><code class="lang-ts"><span class="hljs-comment">// BAD CODE, DO NOT DO THIS</span>
<span class="hljs-keyword">const</span> countCommonNames = (users: User[]): <span class="hljs-function"><span class="hljs-params">CommonNamesAggregate</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> uniqueNames = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>(users.map(<span class="hljs-function">(<span class="hljs-params">{firstName}</span>) =&gt;</span> firstName));
  <span class="hljs-keyword">const</span> commonNamesAggregate = [...uniqueNames].reduce(
    <span class="hljs-function">(<span class="hljs-params">obj, name</span>) =&gt;</span> {
      obj[name] = <span class="hljs-number">0</span>;
      <span class="hljs-keyword">return</span> obj;
    },
    {}
  );

  users.forEach(
    <span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> commonNamesAggregate[user.firstName]++
  );

  <span class="hljs-keyword">return</span> commonNamesAggregate;
};
</code></pre>
<p>This might look simpler because it has fewer lines of code, but it gives the reader less information about the intent of the code. By using <code>forEach</code>, we tell the reader to hold onto their hats and read carefully because we're about to do <em>something</em>. The issue is that the only clues about what that something is, come from code that is mainly outside and before the forEach callback.</p>
<blockquote>
<p><strong>Code smell:</strong> Mutating a local variable from within a <code>forEach</code>.</p>
</blockquote>
<p>Ultimately, forEach is the array function with the least semantic meaning and the least ability to reveal intentions. As such, forEach should be used as a last resort when there isn't a more suitable function or when you explicitly want to indicate that what occurs within the callback is not relevant to the adjacent code.</p>
<p>I also want to talk briefly about a code smell in <code>reduce</code> usage: returning an array from reduce.</p>
<blockquote>
<p><strong>Code smell:</strong> Returning an array from reduce. Consider <code>map</code> or <code>filter</code> instead.</p>
</blockquote>
<p>People usually do this by mistake when they want to chain <code>.filter</code> and <code>.map</code> together, but instead, they do it in one function. The damage from this anti-pattern is that we mislead readers of our code entirely.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>As programmers, we spend a lot of time working with collections of things, whether arrays, sets, maps, or objects. JavaScript comes with a fantastic suite of array functions that can make our days easier and our code more readable. Hopefully, this article has given you the confidence to know which array function to use and why.</p>
<h4 id="heading-tldr">TLDR</h4>
<ul>
<li>Use <code>.map</code> to "transform" an array of one thing into an array of something else</li>
<li><strong>Code smell:</strong> Mutating a local variable from within a <code>map</code>.</li>
<li>Use <code>forEach</code> for creating side-effects beyond the local scope</li>
<li><strong>Code smell:</strong> Using <code>.push</code> inside <code>forEach</code>. Consider alternative e.g. <code>.map</code> or <code>.filter</code></li>
<li><strong>Code smell:</strong> Mutating a local variable from within a <code>forEach</code>.</li>
<li>Use <code>reduce</code> to reduce a collection of elements to a single aggregate entity.</li>
<li><strong>Code smell:</strong> Returning an array from reduce. Consider <code>map</code> and/or <code>filter</code> instead.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Your Microservices Are Slowing You Down, could Domain Services Boost Productivity?]]></title><description><![CDATA[The benefits of a Microservices Architecture are well known; they reduce coupling, allow independent deployments, and increase the rate of change in our applications, making product managers love us, finance teams applaud us, and CEOs offer us big bo...]]></description><link>https://antman-does-software.com/your-microservices-are-slowing-you-down-could-domain-services-boost-productivity</link><guid isPermaLink="true">https://antman-does-software.com/your-microservices-are-slowing-you-down-could-domain-services-boost-productivity</guid><category><![CDATA[software architecture]]></category><category><![CDATA[Microservices]]></category><category><![CDATA[DDD]]></category><category><![CDATA[APIs]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sat, 19 Feb 2022 01:53:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/Xx_d26R37E4/upload/v1645236474215/Hojzvt7Mk.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The benefits of a Microservices Architecture are well known; they reduce coupling, allow independent deployments, and increase the rate of change in our applications, making product managers love us, finance teams applaud us, and CEOs offer us big bonuses. Or so I keep being told at conferences.</p>
<p>However the costs of a Microservices Architecture are not talked about nearly as often as they should be. The trade off when we create a new Microservice is increased technical complexity, increased coupling, dependent deployments, greater risk of introducing distributed transactions, and overall a decreased rate of change in our applications.</p>
<p>Wait a minute! The costs sound almost identical to the benefits, so what's going on here? Some of the dogmatic enthusiasm for Microservices is due to our familiar old friend the causal fallacy. Microservices architectures <em>correlate</em> with other beneficial behaviours, up to a point, beyond which their costs exceed their benefits and the approach begins providing negative value.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643532502199/pBpoNKBKf.png" alt="Blank diagram-2.png" /></p>
<p>This benefit is non-linear, the first few Microservices provide a lot of extra value! But these returns quickly diminish, until the Microservices architecture itself becomes a hindrance, in some instances reducing productivity to the point where change is now harder than it was before. </p>
<p>The irony of the negative value phase is that the system has created a feedback loop where the cost to create a new Microservice is low, and the pain is distributed across teams/engineers in the organisation. No one can solve the problem individually, everyone must agree, but whoever continues creating new Microservices reaps the benefits of another team's good behaviour. Microservices architecture can thereby create a system implementing tragedy of the commons. This is how companies wind up with thousands of Microservices, even after the approach shows its flaws.</p>
<p>My goal in this article is not to persuade people that Microservices Architecture is bad. Instead, I want to expose the root cause behind the initial rise in productivity with Microservices, and then find ways to maintain or extend it. My objective is to create an approach that helps us find and stay at the top of the hill where we have the greatest rate of change. I want to give us a set of tools to know when to apply this architectural approach, and how far to go.</p>
<p>The reason Microservices work so well at first is because, when you break up your system into tiny components, it is highly improbable that a single new service with a single responsibility will cross domain boundaries. Your first Microservice was a success, so you build a few more. Along the way you re-allocate these services between teams quite easily as you intuit where the domain boundaries are in your business. So far so good, and quickly you build up tens, then hundreds, and maybe eventually thousands of Microservices, as Uber did.</p>
<p>This is what I call the shotgun approach to Microservices architecture, and it usually occurs due to a lack of design. If you make the pieces small enough, no one has to do the hard design work of identifying bounded contexts or designing domains up front. Instead you can always trade service ownership between teams. The person who championed the approach in the beginning gets lots of praise as the team climbs the hill in the above diagram.</p>
<p>And then the wheels fall off. The top of the hill is a narrow precipice.</p>
<p>Probably around the time you (accidentally) create your first distributed transaction, you realise your Microservices aren't so decoupled, and perhaps you have built a distributed monolith. Every feature that made them great before, now makes them challenging. If you make a change to a service, you can't reasonably tell which other services are directly or indirectly affected. Observability becomes a nightmare, where a single user interaction may be handled by a dizzying number of services talking to each other. What was once function calls in code has now become distributed network requests that must be traced. An outage in one service results in a set of failures that light up your alerting systems like a Christmas tree, making the root cause a nightmare to diagnose.</p>
<p>Whole <a target="_blank" href="https://istio.io/latest/about/service-mesh/">new solutions are invented</a> to solve problems that teams created for themselves, further increasing complexity and the likelihood of unpredictable emergent behaviour creating incidents of change failure, which themselves become increasingly difficult to solve. </p>
<p>You might be building and deploying small Microservices independently, but the meaningful non-functional requirements live at the system level. You can't reasonably test the suitability of a Microservice deployment in isolation, because the system behaviour is contingent on the whole. As we reduced the responsibilities of a service down to a single purpose, we too reduced it's overall contribution to the -ilities that make our system manageable.</p>
<p>Likewise, Microservices may be deployed independently, but meaningful, value-adding change that the business asks for requires changes to multiple Microservices. With too many services, these changes themselves cascade across services. You've gone from one deployment containing many changes, to one change requiring many deployments.</p>
<p><strong>What's the alternative?</strong> Like abstract versus concrete classes, decoupling vs cohesion, the answer is applying things appropriately, and moderately. The key benefit of the first few Microservices is that we create an architecture where teams are empowered to deploy code changes that align with the axis of change in the business. So how do we define moderate and appropriate?</p>
<p>Typically, the first system we split out is one that is obviously discrete, such authentication or payments. The boundary between the new service and the old service is easy to identify: a small surface area of interaction, either side of which the two services are relatively independent. It's easy to get right, hard to get wrong the first time. But taking this first win and going from a crawl to a sprint is ill advised.</p>
<p>Instead, take the time to do planning and analysis before creating your Microservices. This planning typically has to occur sometime after the business has actually begun operating. Planning a Microservices architecture in a greenfield product before achieving product market fit is a sure way to get the bounded context wrong. You're aiming for a fast moving target. During the incubation phase, there's more to be gained by focusing on building differentiators and buying the rest than there is in architecting Microservices.</p>
<p>However with a product in the growth or extraction phase, you can analyse existing code in a well architected monolith, and <a target="_blank" href="http://www.codingthearchitecture.com/2014/07/06/distributed_big_balls_of_mud.html">don't split your services if you can't create a well architected monolith first</a>. Look for clear business domains with very few dependencies on other parts of the code. These are vertical slices of the code you are likely to be asked to change by a product manager, and would be relatively easy to do so in one atomic change.</p>
<p>One technique is to go through Jira comparing past tickets to the code base. The crucial factor here is to align your domains along the axis of change, and our ticketing system is a great way to identify what tends to change discretely and what changes dependently. </p>
<p>You see, the rate of change is determined by the number of dependencies affected by that change. This is true in terms of code AND organisation; that is, your team structure should reflect your business domains which should reflect your code and your architecture. The more these four elements align, the lower the resistance and the faster the rate of change.</p>
<p>The perfect example of this is simply aligning services or teams along the wrong axis. That is, if you have a frontend Microservice, an API gateway Microservice, a few business logic services, and something crazy like a database service. Then imagine that each service had its own team. In this (slightly) contrived example, we have built teams and services around the layers of our application, rather than vertical slices. Since no change can occur without affecting all layers, teams AND services have to communicate a lot in order to deliver change or value.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644154266409/JwZ2xqlD-d.png" alt="Blank diagram-5.png" /></p>
<p>A Microservices Architecture may start out looking like the diagram on the right, but if you're not careful, the quest for smaller and smaller services will inadvertently lead teams to create something akin to the diagram on the left. Remember, you're optimising for changeability, <em>not</em> reusability. They are two competing goals!</p>
<p>What you ultimately want to do is apply <a target="_blank" href="https://martinfowler.com/bliki/DomainDrivenDesign.html">Domain Driven Design</a> to find a small number of discrete, well bounded domains. Then you can structure your teams, code, architecture, and business around these domains.</p>
<p>I believe that finding the correct name for this approach and its services is crucial. The term Microservices seems to imply that more and smaller is better. However as we have seen, discrete and well bounded is far more important than size or number. That is why I have chosen to call them <strong>Domain Services</strong><a class="post-section-overview" href="#Footnotes">*</a>. This name reveals our true objective; aligning teams, code, and architecture with business <strong><em>domains.</em></strong></p>
<p>My rule of thumb is that the number of Domain Services an engineering department should aim for is the number of feature engineers divided by three or five. That means a team of 10 engineers developing features should have 2 or 3 Domain Services, not 20! A team of 30 feature engineers could have 6 to 10 Domain Services.</p>
<p>This ensures that the services are large enough that they are easy to maintain transaction boundaries within them. The number of discrete domain boundaries that you have to find is reduced, and teams only have to create and maintain a contract/context map for the entities that are communicated across that exposed boundary, keeping communication costs low while greatly enhancing rate of change.</p>
<p>You may have heard that "Microservices should do only one thing!" but I would argue that this only <em>seems</em> true because it implicitly enforces the more accurate rule:</p>
<blockquote>
<p>A domain must only be implemented in one service, but a service <em>may</em> implement more than one domain</p>
</blockquote>
<p>This matches its sister rule in Domain Driven Design:</p>
<blockquote>
<p>A domain must only be worked on by one team, but a team may work on more than one domain</p>
</blockquote>
<p>Each Domain Service you add should be planned and executed with extreme caution! Every time you add a service, you increase the number of <strong>exposed domain boundaries</strong> rapidly. An exposed boundary is a domain boundary that is implemented across the network, between services. Every <em>exposed</em> boundary introduces an API surface that enforces a contract. A contract must be maintained or versioned through changes. </p>
<p>The more boundary exposure you have, the slower your rate of change behind that boundary. Heuristically speaking this means that if your service is too small, the ratio of behavioural code and API code tilts toward API code. As it does the utility of the service approaches zero, since any change in behaviour will more likely result in a change in API. The more Microservices you have, the smaller their behavioural code, and the greater their boundary exposure, soon every Microservice is more API than behaviour and productivity halts.</p>
<p>The reason we began creating Microservices in the first place was to increase the rate of change in behavioural code — a benefit we lose as we add more services. The business doesn't ask us to change API code, the business asks us to change the behaviour of the system. The API is meant to help facilitate that change, not hinder it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645233763466/OFMbzZaVWK.png" alt="Behavioural vs API Code" /></p>
<p>For example, two Domain Services can have only one boundary and zero possible dependent services, three services gives us three boundaries, four services gives us up to six, and so on. This is without considering indirect dependencies, which can accumulate even faster.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Services</td><td>Boundaries</td><td>Indirect Dependencies</td></tr>
</thead>
<tbody>
<tr>
<td>2</td><td>1</td><td>0</td></tr>
<tr>
<td>3</td><td>3</td><td>3</td></tr>
<tr>
<td>4</td><td>6</td><td>12</td></tr>
<tr>
<td>5</td><td>10</td><td>30</td></tr>
</tbody>
</table>
</div><p>The indirect dependencies column describes how a command from one service to another could subsequently depend on the remaining services. That is, if we have 4 services, for each service one of those services could issue commands to, each of those could depend on the two remaining services. When all of those possible dependencies are accounted for at a single depth, we have 12 opportunities for dependent calls.</p>
<p>Of course, these numbers represent the upper bound, the worst case scenario. Importantly they show how too many Microservices quickly turns into a new <a target="_blank" href="https://en.wikipedia.org/wiki/Big_ball_of_mud">big ball of mud</a> — the very thing we wanted to avoid, just with added network, concurrency, and deployment issues.</p>
<p>As you can see from the above table, the change from two Domain Services to three is almost as important as the change from one to two because a third service introduces new <em>types</em> of complexity. You will likely be carving this new Domain Service out of an existing one, so you must be thorough. Check that the candidate Domain Service doesn't cross transaction boundaries, investigate all the events and commands within the domain, and work with your product manager and/or business analysts to ensure your model of the domain matches theirs.</p>
<p>Modelling domains as a collection of commands, events, and aggregates, entities, or projections helps a lot with this process. Even if the code here is well established, it can be worth running an event storming session.</p>
<p>At this point in the article it is worth running through a definition of commands and events, because understanding the distinction between the two is critical. The simplest definition I have come up with is this one:</p>
<blockquote>
<p>A command is a request to change the persisted state; An event is the delta of that change.</p>
</blockquote>
<p>That is, every time you would write to a database, whether that is an insert, update, or delete, emit an <strong>event</strong> which describes what changed (NOT the new state!) An event cannot be rejected because it is a historical statement of fact. It might not be a happy fact, but it is one nonetheless.</p>
<p>Every time you have the <em>intent</em> of changing state, issue a <strong>command</strong>, knowing that it could be rejected. Commands can have outcomes, which might be the reason the command was rejected, or it might be acknowledgement that the command was accepted. When a command is accepted, an event <strong><em>must</em></strong> eventually follow.</p>
<p>It's important to note that events <em>are</em> the transaction boundary. In an event based system with multiple services, your system has to be and expect eventual consistency. </p>
<p>Let's say your checkout service issues a <code>CompletePurchase</code> command, and checks its simplified inventory data before accepting the command. It then emits a <code>PurchaseCompleted</code> event to the inventory service,which has a much more sophisticated understanding of inventory. At the same time, the inventory service has sent a <code>StockAdjusted</code> event because someone has reported an item as damaged at the warehouse. The inventory service now realises that it can't service the order because the available stock is no longer available. Should it reject the <code>PurchaseCompleted</code> event?</p>
<p>No. The purchase happened, the user's credit card has been charged. This is a matter of historical fact and cannot be rejected. You might say we should have prevented the purchase then, "if only we had kept these domains together!" you lament. But remember, the inventory domain only updated its available inventory count <em>after</em> the damage physically occurred in the warehouse. The real world itself is eventually consistent anyway!</p>
<p>In this example, you can see that the domain events are the true transaction boundary. A <code>PurchaseCompleted</code> event might lead to an <code>ItemAllocated</code> event from the inventory domain in the green path, reducing the available stock count, but the fact that these two events are correlated does not make them an atomic action.</p>
<p>If you model your domains this way, you will find that the domain boundaries are quite clear, and that the events and commands that are shared between them define the API boundaries between our Domain Services.</p>
<p>The reason this distinction between commands and events is crucial is because it relates to another key rule of Domain Services:</p>
<blockquote>
<p>Communication between Domain Services should consist of events, not commands</p>
</blockquote>
<p>If your Domain Services are issuing lots of commands to each other, this is an indication that your domain boundary is incorrectly placed. A single user interaction should result in a command within a single Domain Service. The primary Domain Service handling the initial command may dispatch one or more events to other Domain Services, which may themselves issue commands to themselves <em>on receipt</em> of the event (but not when replaying the event from their own event store). However these must be events, not commands, and must fail independently.</p>
<p>The result is that a user interaction may be satisfied by a single Domain Service without issuing dependent commands. This does not however, preclude additional responses from the events occurring asynchronously. E.g. if the Checkout Service emits a <code>PurchaseCompleted</code> event then the Notification Service may email the user an invoice based on the event it received and their preferences it has stored. The Dispatch Service may issue a <code>CreateFulfilmentOrder</code> command and begin the process of shipping goods to the user. Each of these commands may fail, but they fail independently.</p>
<p>Let's compare this to a shotgun Microservices Architecture implementation. In the shotgun approach, the <code>CompletePurchase</code> "command" may be a call to a Microservice that validates the cart, which then calls another service to check the billing, while also calling another service to reduce the stock levels, and another to create a carrier label for shipping, another service to update the users order history, and another service to email the invoice. All of these calls may be commands to other services, and all of them may fail in dependent ways which are then (hopefully) fed back to the user.</p>
<p>Coming back to the crux of this article, I am confident that a team of 35 developers working across 7 Domain Services will in the majority of cases, outperform a team of 35 developers working across 60 Microservices. Of course, no quantitative measure exists, but the stories of <a target="_blank" href="https://eng.uber.com/microservice-architecture/">development teams reducing and reorganising their Microservices</a> provides qualitative evidence to support my hypothesis, in addition to my personal experience, and deductive reasoning outlined in this article.</p>
<p>Ultimately the goal of any architecture is to facilitate ease of change into the future, without sacrificing our target non-functional requirements. This architectural objective is predicated on our ability to predict the nature of changes into the future. Of course, all predictions are imperfect, and increasingly so the further our outlook. However I believe Domain Services better aligns our architecture with our team structure and business structure by utilising tools such as Domain Driven Design to identify numerous independent (or at least loosely coupled) axes of change in our systems.</p>
<h2 id="heading-summary-of-heuristics-and-rules">Summary of Heuristics and Rules</h2>
<p>We covered a lot of ideas in this article, so we will quickly recap the key thoughts. I have split these into heuristics and laws. </p>
<p>The heuristics are rules of thumb that are <em>usually</em> true in any software system with "services", whether that is Microservices or Domain Services. I find these are useful ideas to keep in mind when consider the trade offs of introducing new services.</p>
<p>The laws are rules that define best practices for a Domain Services approach, and thus uses the language of <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc2119">RFC 2119</a></p>
<h3 id="heading-heuristic-of-multi-service-systems">Heuristic of Multi-Service Systems</h3>
<ul>
<li>As new services are added, complexity increases non-linearly</li>
<li>As new services are added, rate of change increases by <em>at most</em> +1</li>
<li>The total rate of change increases as the deployment to change ratio approaches 1:1</li>
<li>The rate of change is reduced by the number of dependent/affected systems/teams for each change</li>
<li>The utility of a service is its amount of behavioural code compared to API code</li>
<li>Increasing the number of services reduces the amount of behavioural code in each service </li>
<li>Increasing the number of services increases the amount of API code in each service</li>
</ul>
<h3 id="heading-the-laws-of-domain-services">The Laws of Domain Services</h3>
<ul>
<li>A domain must only be implemented in one Domain Service, but a Domain Service may implement more than one domain</li>
<li>A domain must be owned by only one team, but one team may own more than one domain</li>
<li>Domain Services must only communicate asynchronously</li>
<li>Domain Services must communicate via events</li>
<li>Domain Services should not dispatch commands to other services</li>
<li>Events must be an atomic transactional boundary</li>
<li>Events must never be rejected (but they can be ignored)</li>
<li>Commands must be a rejectable request to change the persisted state</li>
<li>Events must be a semantic encapsulation of the delta of altered state, and an immutable historical fact</li>
<li>An accepted command must produce an event</li>
<li>A Domain Service may issue a command to itself when it ingests an event from another Domain Service</li>
<li>Domain Services must not share persistence layers</li>
<li>A Domain Service should only receive data it requires from other Domain Services via events</li>
<li>A Domain Service should not request additional data from another Domain Service in order to handle a command</li>
</ul>
<hr />
<h4 id="heading-footnotes">Footnotes</h4>
<p>* Fans of Domain Driven Design, especially those whom follow the Object Orientated implementation, might recognise the term as an implementation level pattern in code. Given the term isn't referenced in <a target="_blank" href="https://www.amazon.com/Domain-Driven-Design-Distilled-Vaughn-Vernon/dp/0134434420">DDD: Distilled</a>, seems to cause confusion, and isn't well distinguished from other DDD patterns, I feel that we can make better use of this term at the architecture level. Therefore I have stolen and repurposed it, sorry not sorry.</p>
]]></content:encoded></item><item><title><![CDATA[Deliver a Meaningful Tech Strategy While Still Shipping Features with The Three Stream Backlog]]></title><description><![CDATA[It's a story as old as time, boy meets girl, girl falls in love with incomprehensible eldritch horror — oh wait no, sorry wrong story, this one is about TECH DEBT! 
In this painfully familiar story, before every other sprint, stakeholders in the busi...]]></description><link>https://antman-does-software.com/the-three-stream-backlog</link><guid isPermaLink="true">https://antman-does-software.com/the-three-stream-backlog</guid><category><![CDATA[Scrum]]></category><category><![CDATA[technology]]></category><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sun, 21 Nov 2021 07:15:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1637478289045/mydSaRWMjj.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It's a story as old as time, boy meets girl, girl falls in love with incomprehensible eldritch horror — oh wait no, sorry wrong story, this one is about TECH DEBT! </p>
<p>In this painfully familiar story, before every other sprint, stakeholders in the business raise some crucial feature that needs to be developed as quickly as possible. The business proclaims "it's an emergency!" (it isn't), and the team is asked to take on more technical debt and told "don't worry, we'll make time to deal with it later". Every day since has been "today" and not one of them seems to be this day called "later". The engineers have stopped wondering if later will ever come...</p>
<p>Over time, the team's productivity asymptotically approaches zero. Product managers are getting frustrated, engineers are frustrated, stakeholders have given up expecting anything from Product (unless they declare it an emergency), and the tech debt seems insurmountable. Developing new features is neither easy nor enjoyable. Everyone has a different solution, from re-writes to an entire quarter without feature work.</p>
<p>In this article, not only do I offer a viable way out of the morass, but a strategy to ensure things don't get this bad in the first place. It's outstandingly simple, and requires only a little focus to execute on. I call it <strong>The Three Stream Backlog</strong></p>
<h2 id="heading-the-what-now">The what now?</h2>
<p>If this idea catches on I'm going to really regret not coming up with a better name, but <strong>The Three Stream Backlog</strong> is dividing your backlog up into well... three streams of work.</p>
<h3 id="heading-feature-work">Feature Work</h3>
<p>This stream contains the epics that Product Managers and the team have poured blood, sweat, and tears into developing and preparing for development. The epics in this stream have been through discovery, user research, user testing, prototyping, tech spikes, technical feasibility planning, etc. The team is confident that these epics are a good use of engineering resources.</p>
<p>We're not going to spend too long talking about this stream because the entire Product Management discipline is devoted to doing this well.</p>
<p>Allocate 50 - 70% of the team's engineering resources to this stream.</p>
<h3 id="heading-business-as-usual">Business as Usual</h3>
<p>This stream contains only <em>tickets</em> for bug fixes, copy improvements, legal document updates, and minor changes. There are no epics in this stream! It's really important that large features do not try to sneak in.</p>
<p>Allocate 20 - 25% of the team's engineering resources to this stream.</p>
<h3 id="heading-technical-work">Technical Work</h3>
<p>This stream contains both tech debt tickets AND epics. However, these epics are created by engineering leadership with the same rigor, research, planning, and confidence in deliverable value as the epics in the feature stream.</p>
<p>This means engineering leadership need to learn some product management skills and maintain their own backlog. The crucial step here is really stepping back and looking at the big picture. Make sure your tech backlog supports and empowers the team to deliver on the feature roadmap. </p>
<p>Does that mean you need to plan an upgrade to Hasura to support web hooks for the upcoming integration feature? Do you need to implement a repeatable solution to building audit tables alongside the mutable tables that Data &amp; Analytics teams are relying on for their reports? Is the developer experience slowing everyone down and causing "It works on my machine!" bugs? Maybe deployments are too difficult and error prone.</p>
<p>The tech stream is partially recursive, because you will find it contains a stream of big features and smaller bug fixes, just like the three streams themselves. You need to put on your Product Management hat and figure out how to efficiently allocate limited resources to seemingly unlimited problems. This leads me to my next point.</p>
<h2 id="heading-executing-on-the-three-streams">Executing on The Three Streams</h2>
<p>The critical detail in implementing this effectively is allocating people to particular streams for entire sprints. Context switching kills focus, kills productivity, and saps our team's morale.</p>
<p>So given a team of 4 engineers, you would allocate them to the 3 streams as such:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Resources</td><td>Stream</td></tr>
</thead>
<tbody>
<tr>
<td>2x Engineers</td><td>Feature</td></tr>
<tr>
<td>1x Engineer</td><td>BAU</td></tr>
<tr>
<td>1x Engineer</td><td>Tech</td></tr>
</tbody>
</table>
</div><p>This focus is the critical detail that lets you plan big, empowering tech epics and deliver serious improvements to the infrastructure, developer experience, and non-functional requirements of the product! You now have dedicated resources each sprint to work on the tech.</p>
<p>What's the alternative? I think we have all tried the "30% of the sprint's story points are for tech debt tickets" approach. Has this ever worked for anyone? The problems with that approach are:</p>
<ul>
<li>You can't plan significant tech work</li>
<li>Tech debt tickets are prioritised last in the sprint, meaning they get done in a rushed and haphazard way, if picked up at all</li>
<li>You can't predict delivery of tech work</li>
<li>Often, no one is really looking after these tickets or planning the tech backlog</li>
</ul>
<p>In the end, the story point approach leaves the tech team feeling even more disempowered, and the technical challenges seemingly even more insurmountable.</p>
<h3 id="heading-tips-and-tricks">Tips and Tricks</h3>
<h4 id="heading-win-over-product-managers">Win over Product Managers</h4>
<p>Product Managers might push back on the idea of them handing over up to 25% of the available engineering resources every sprint for tech to work on their own backlog. However, remind them that this means they don't have to prioritise tech debt tickets ever again. It means the team will deliver faster and more efficiently. It means the team will be able to estimate work better because the code is easier to work with. It means deployments will become more reliable and less stressful. </p>
<p>Try it for a quarter and ensure you deliver a significant technical challenge that improves your ability to deliver Product features reliably. Then, tell <em>everyone</em> that will listen that this was possible because you were able to plan technical work via the three stream backlog and deliver it with uninterrupted focus. When I first introduced it, my team introduced Continuous Deployment for the first time in a business that many thought would never pull it off.</p>
<h4 id="heading-avoid-stream-fatigue">Avoid Stream Fatigue</h4>
<p>It's important to swap people around between the feature, BAU, and tech streams. Everyone needs that shared context, and often the things people learn working in one stream will make them more effective in another. </p>
<p>However, be mindful not to break peoples' flow. Let people stay in the tech stream until their technical epic is complete, and same for the feature stream. Since the BAU stream is made up entirely of small tickets, it's easier to swap people in and out of this stream between sprints than the feature and tech streams.</p>
<h4 id="heading-report-on-outcomes-with-discipline">Report on Outcomes with Discipline</h4>
<p>As a new Product Manager of the technical backlog, you need to put in place some systems to measure the success of your tech epics. This means your epics need to not only be well formed coming in, but well analysed on the way out. I do this by creating a report in Confluence for each big feature, and include:</p>
<ul>
<li>What went well</li>
<li>What could be improved next time</li>
<li>What outcomes occurred that I expected (i.e. reduced lead time, increase release frequency, better team health metrics, etc)</li>
<li>What outcomes occurred that I did not expect</li>
</ul>
<p>When people ask you if this approach is working, you will have a repository of delivered work and measured outcomes to direct them towards.</p>
<h3 id="heading-process-compatibility">Process Compatibility</h3>
<p>So far I've seen the three streams work best on agile teams running Scrum with sprints. It works decently on Kanban teams, but swapping engineers between streams gets a little messy.</p>
<p>It is not compatible with Shape Up, but then I believe Shape Up is incompatible with life, healthy teams, kittens, and basically anything good and bright in the world.</p>
]]></content:encoded></item><item><title><![CDATA[Stop catching errors in TypeScript; Use the Either type to make your code predictable]]></title><description><![CDATA[In some languages such as Java, methods or functions can provide type information about the Exceptions or Errors they may throw. However in TypeScript, it is not possible to know what Errors a function may throw. In fact, a function could throw any v...]]></description><link>https://antman-does-software.com/stop-catching-errors-in-typescript-use-the-either-type-to-make-your-code-predictable</link><guid isPermaLink="true">https://antman-does-software.com/stop-catching-errors-in-typescript-use-the-either-type-to-make-your-code-predictable</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Functional Programming]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sat, 23 Oct 2021 13:35:20 GMT</pubDate><content:encoded><![CDATA[<p>In some languages such as Java, methods or functions can provide type information about the Exceptions or Errors they may throw. However in TypeScript, it is not possible to know what Errors a function may throw. In fact, a function could throw <em>any</em> value, even a string, number, object, etc. This is why TypeScript types caught values as <code>unknown</code> since v4.4.</p>
<p>So what do we do about all the "negative" outcomes of a function call? If I am writing a <code>handleLoginUser</code> function, and the supplied password is invalid, or the account isn't active, how do I implement this effectively in TypeScript?</p>
<p>A lot of programmers who are new to TypeScript might implement these cases as classes extending <code>Error</code> and then throw those classes. This approach has some flaws, let's take a look:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handleLoginUser = <span class="hljs-keyword">async</span> (username: <span class="hljs-built_in">string</span>, password: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">Promise</span>&lt;User&gt; =&gt; {
  <span class="hljs-keyword">if</span> (username === <span class="hljs-string">''</span>) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> EmptyUsernameLoginError();
  }
  <span class="hljs-keyword">if</span> (password === <span class="hljs-string">''</span>) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> EmptyPassswordLoginError();
  }
  <span class="hljs-keyword">if</span> (isCorrectUserPasswordCombo(username, password) === <span class="hljs-literal">false</span>) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidCredentialsError();
  }
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> getUserbyUsername(username);
  <span class="hljs-keyword">if</span> (user.active === <span class="hljs-literal">false</span>) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InactiveUserError();
  }
  <span class="hljs-keyword">return</span> user;
}
</code></pre>
<p>Most of this function is actually about the things that can go wrong, but our types only inform us of the successful path. That means 4/5ths of the function's output is untyped!</p>
<p>The above "exceptions" or "errors" aren't really exceptions or errors at all. They are outcomes. They are predictable, reasonable parts of our system. My heuristic is, if they are something a good product manager would care about, they are not exceptions and you shouldn't throw them!</p>
<p>Exceptions are unpredictable things we cannot reasonably plan for, that the system should not attempt recovery from, and we should not route to the user. </p>
<p>There is an issue in the above code that is less obvious at first glance; If the code calling <code>handleLoginUser</code> is catching these errors and using them to display messages to the user, then there is a real possibility that a serious system issue could get caught in the same error handling code and displayed to the user.</p>
<p>Not only is it a terrible user experience to get a stack trace in the UI, it is a security risk too. Don't make exceptions handling code responsible for both business cases AND exceptions!</p>
<h3 id="typing-the-red-paths">Typing the red paths</h3>
<p>How could we make these different outcomes more visible in the type system? One option is to build a <a target="_blank" href="https://antman-does-software.com/typescripts-discriminated-unions-with-real-use-cases">discriminated union</a> of outcomes. Another, complementary approach, is to use an <code>Either</code></p>
<p>An <code>Either</code> is a data type that holds some value in a property called <code>left</code> OR some value in a property called <code>right</code>, but never both at once, and never neither. In set theory we would call it a disjoint union, as opposed to the typical union <code>|</code> we might use to create an optional type <code>type Optional&lt;T&gt; = T | undefined</code>. </p>
<p>Let's have a look at how we would define an Either in TypeScript:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> Left&lt;T&gt; = {
  left: T;
  right?: <span class="hljs-built_in">never</span>;
};

<span class="hljs-keyword">type</span> Right&lt;U&gt; = {
  left?: <span class="hljs-built_in">never</span>;
  right: U;
};

<span class="hljs-keyword">type</span> Either&lt;T, U&gt; = NonNullable&lt;Left&lt;T&gt; | Right&lt;U&gt;&gt;;
</code></pre>
<p>Nothing ground shattering so far, now TypeScript will let us define a left value or a right value but never both:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> validLeft: Either&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">string</span>&gt; = {left: <span class="hljs-string">'foo'</span>}; <span class="hljs-comment">// valid</span>

<span class="hljs-keyword">const</span> validRight: Either&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">string</span>&gt; = {right: <span class="hljs-string">'foo'</span>}; <span class="hljs-comment">// valid</span>

<span class="hljs-keyword">const</span> invalidBoth: Either&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">string</span>&gt; = {left: <span class="hljs-string">'foo'</span>, right: <span class="hljs-string">'bar'</span>}; <span class="hljs-comment">// Invalid, won't compile</span>
</code></pre>
<p><strong>Why <code>left</code> and <code>right</code>? What gives?</strong> The convention is that left is used for failure cases and the right hand side is used for success cases. The reason is actually that "right" is a pun or synonym for correct. </p>
<p>When Either's are used for success/failure paths, they are called biased Either's. When they hold two potential types for a purpose unrelated to success or failure, they are referred to as an unbiased Either. For the rest of this article we will focus on the biased Either.</p>
<p>Let's add a few helper functions so that we can more easily use the <code>Either</code> type in our <code>handleLoginUser</code> command:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> UnwrapEither = &lt;T, U&gt;<span class="hljs-function">(<span class="hljs-params">e: Either&lt;T, U&gt;</span>) =&gt;</span> NonNullable&lt;T | U&gt;;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> unwrapEither: UnwrapEither = &lt;T, U&gt;<span class="hljs-function">(<span class="hljs-params">{
  left,
  right,
}: Either&lt;T, U&gt;</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (right !== <span class="hljs-literal">undefined</span> &amp;&amp; left !== <span class="hljs-literal">undefined</span>) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(
      <span class="hljs-string">`Received both left and right values at runtime when opening an Either\nLeft: <span class="hljs-subst">${<span class="hljs-built_in">JSON</span>.stringify(
        left
      )}</span>\nRight: <span class="hljs-subst">${<span class="hljs-built_in">JSON</span>.stringify(right)}</span>`</span>
    );
    <span class="hljs-comment">/*
     We're throwing in this function because this can only occur at runtime if something 
     happens that the TypeScript compiler couldn't anticipate. That means the application
     is in an unexpected state and we should terminate immediately.
    */</span>
  }
  <span class="hljs-keyword">if</span> (left !== <span class="hljs-literal">undefined</span>) {
    <span class="hljs-keyword">return</span> left <span class="hljs-keyword">as</span> NonNullable&lt;T&gt;; <span class="hljs-comment">// Typescript is getting confused and returning this type as `T | undefined` unless we add the type assertion</span>
  }
  <span class="hljs-keyword">if</span> (right !== <span class="hljs-literal">undefined</span>) {
    <span class="hljs-keyword">return</span> right <span class="hljs-keyword">as</span> NonNullable&lt;U&gt;;
  }
  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(
    <span class="hljs-string">`Received no left or right values at runtime when opening Either`</span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> isLeft = &lt;T, U&gt;(e: Either&lt;T, U&gt;): e is Left&lt;T&gt; =&gt; {
  <span class="hljs-keyword">return</span> e.left !== <span class="hljs-literal">undefined</span>;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> isRight = &lt;T, U&gt;(e: Either&lt;T, U&gt;): e is Right&lt;U&gt; =&gt; {
  <span class="hljs-keyword">return</span> e.right !== <span class="hljs-literal">undefined</span>;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> makeLeft = &lt;T&gt;(value: T): Left&lt;T&gt; =&gt; ({ left: value });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> makeRight = &lt;U&gt;(value: U): Right&lt;U&gt; =&gt; ({ right: value });
</code></pre>
<h3 id="applying-either-to-handleloginuser">Applying <code>Either</code> to <code>handleLoginUser</code></h3>
<p>Let's take a look at our new implementation of <code>handleLoginUser</code> when we return an Either instead of throwing:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> LoginError =
  | <span class="hljs-string">'EMPTY_USERNAME'</span>
  | <span class="hljs-string">'EMPTY_PASSWORD'</span>
  | <span class="hljs-string">'INVALID_CREDENTIALS'</span>
  | <span class="hljs-string">'INACTIVE_USER'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handleLoginUser = <span class="hljs-keyword">async</span> (username: <span class="hljs-built_in">string</span>, password: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">Promise</span>&lt;Either&lt;LoginError, User&gt; =&gt; {
  <span class="hljs-keyword">if</span> (username === <span class="hljs-string">''</span>) {
    <span class="hljs-keyword">return</span> makeLeft(<span class="hljs-string">'EMPTY_USERNAME'</span>);
  }
  <span class="hljs-keyword">if</span> (password === <span class="hljs-string">''</span>) {
    <span class="hljs-keyword">return</span> makeLeft(<span class="hljs-string">'EMPTY_PASSWORD'</span>);
  }
  <span class="hljs-keyword">if</span> (isCorrectUserPasswordCombo(username, password) === <span class="hljs-literal">false</span>) {
    <span class="hljs-keyword">return</span> makeLeft(<span class="hljs-string">'INVALID_CREDENTIALS'</span>);
  }
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> getUserbyUsername(username);
  <span class="hljs-keyword">if</span> (user.active === <span class="hljs-literal">false</span>) {
    <span class="hljs-keyword">return</span> makeLeft(<span class="hljs-string">'INACTIVE_USER'</span>);
  }
  <span class="hljs-keyword">return</span> makeRight(user);
}
</code></pre>
<p>The first thing you should notice is that we have types for every possible case in our function. The great thing about this is that now the caller can see every possible outcome in the function return type. It's clear to the caller that they will get a <code>User</code> object if the function succeeds, or one of 4 possible failure types.</p>
<p>Let's have a look at the caller code:</p>
<pre><code class="lang-ts">app.post(<span class="hljs-string">'/login'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { username, password } = req.body;
  <span class="hljs-keyword">const</span> loginEither = <span class="hljs-keyword">await</span> handleLoginUser(username, password);
  <span class="hljs-keyword">if</span> (isRight(loginEither)) {
    <span class="hljs-keyword">const</span> user = unwrapEither(loginEither);
    res.json({ user });
    <span class="hljs-keyword">return</span>;
  }
  <span class="hljs-keyword">const</span> error = unwrapEither(loginEither);
  <span class="hljs-keyword">switch</span> (error) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">'EMPTY_USERNAME'</span>: {
      res.json({ error: <span class="hljs-string">'You must supply a username to login'</span> });
      <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">'EMPTY_PASSWORD'</span>: {
      res.json({ error: <span class="hljs-string">'You must supply a password to login'</span> });
      <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">'INVALID_CREDENTIALS'</span>: {
      <span class="hljs-keyword">const</span> attemptsRemaining = <span class="hljs-keyword">await</span> handleInvalidCredentialsAttempt(username);
      <span class="hljs-keyword">if</span> (attemptsRemaining === <span class="hljs-number">0</span>) {
        res.json({ error: <span class="hljs-string">'You have made too many attempts and been locked out.'</span> });
        <span class="hljs-keyword">return</span>;
      }
      res.json({
        error: <span class="hljs-string">`Invalid username and/or password, you have <span class="hljs-subst">${attemptsRemaining}</span> attempts remaining`</span>,
      });
      <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">'INACTIVE_USER'</span>: {
      res.json({ error: <span class="hljs-string">'Check your email for an activation link'</span> });
      <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">default</span>: {
      isStrictNever(error);
    }
  }
});
</code></pre>
<p>The above code is much more maintainable. We get great type hinting in our switch statement, and <a target="_blank" href="https://antman-does-software.com/strict-and-weak-exhaustive-checks-in-typescript">exhaustiveness checks</a>. Meanwhile, the green path is <em>lean</em>. </p>
<p>Of course, one of the great things about this approach is that we could clearly refactor these into a handler function for the success case, and a handler function for the failure case if we wanted to. In fact, let's do that now:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handleLoginError = <span class="hljs-keyword">async</span> (loginError: LoginError): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">string</span>&gt; =&gt; {
  <span class="hljs-keyword">switch</span> (error) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">'EMPTY_USERNAME'</span>: {
      <span class="hljs-keyword">return</span> <span class="hljs-string">'You must supply a username to login'</span>;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">'EMPTY_PASSWORD'</span>: {
      <span class="hljs-keyword">return</span> <span class="hljs-string">'You must supply a password to login'</span>;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">'INVALID_CREDENTIALS'</span>: {
      <span class="hljs-keyword">const</span> attemptsRemaining = <span class="hljs-keyword">await</span> handleInvalidCredentialsAttempt(username);
      <span class="hljs-keyword">if</span> (attemptsRemaining === <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-string">'You have made too many attempts and been locked out.'</span>;
      }
      <span class="hljs-keyword">return</span> <span class="hljs-string">`Invalid username and/or password, you have <span class="hljs-subst">${attemptsRemaining}</span> attempts remaining`</span>;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">'INACTIVE_USER'</span>: {
      <span class="hljs-keyword">return</span> <span class="hljs-string">'Check your email for an activation link'</span>;
    }
    <span class="hljs-keyword">default</span>: {
      isStrictNever(error);
    }
  }
}
</code></pre>
<p>This function will handle our error cases and return an error string for the user. Let's implement the improved function in our route handler:</p>
<pre><code class="lang-ts">app.post(<span class="hljs-string">'/login'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { username, password } = req.body;
  <span class="hljs-keyword">const</span> loginEither = <span class="hljs-keyword">await</span> handleLoginUser(username, password);
  <span class="hljs-keyword">if</span> (isRight(loginEither)) {
    <span class="hljs-keyword">const</span> user = unwrapEither(loginEither);
    res.json({ user });
    <span class="hljs-keyword">return</span>;
  }
  <span class="hljs-keyword">const</span> loginError = unwrapEither(loginEither);
  res.json({ error: <span class="hljs-keyword">await</span> handleLoginError(loginError) });
});
</code></pre>
<p>That route handler is pretty easy to read at a glance now, and we've removed the mixed responsibilities and mixed levels of abstraction. The route handler maps the request payload to the command handler <code>handleLoginUser</code>, and then maps the outcomes to responses. It now has no business logic of its own, it is not responsible for managing attempts or checking passwords.</p>
<p>Our type system gave us the confidence we needed to easily refactor this code. It's easy to test, and we have managed to decouple our success types from our failure types. This means we don't always have to handle the entire discriminated union of possible outcomes, changing the error types only affects <code>left</code> sided code, and likewise changing the <code>User</code> type only affects <code>right</code> sided code.</p>
<p>Try out the <code>Either</code> pattern in your own functional TypeScript code and let us know how you went in the comments!</p>
]]></content:encoded></item><item><title><![CDATA[Strict & Weak Exhaustive Checks in TypeScript: Nuke your app at runtime for fun and profit!]]></title><description><![CDATA[This article assumes you are familiar with the never type, exhaustive checks, and the concept of failing fast. If you aren't yet, or want a refresher, here are some resources to get up to speed:

When to use never and unknown in TypeScript
The power ...]]></description><link>https://antman-does-software.com/strict-and-weak-exhaustive-checks-in-typescript</link><guid isPermaLink="true">https://antman-does-software.com/strict-and-weak-exhaustive-checks-in-typescript</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Redux]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sun, 17 Oct 2021 02:34:40 GMT</pubDate><content:encoded><![CDATA[<p>This article assumes you are familiar with the never type, exhaustive checks, and the concept of failing fast. If you aren't yet, or want a refresher, here are some resources to get up to speed:</p>
<ul>
<li><a target="_blank" href="https://blog.logrocket.com/when-to-use-never-and-unknown-in-typescript-5e4d6c5799ad/">When to use <code>never</code> and <code>unknown</code> in TypeScript</a></li>
<li><a target="_blank" href="https://www.fullstory.com/blog/discriminated-unions-and-exhaustiveness-checking-in-typescript/">The power of discriminated unions and exhaustiveness checking in Typescript</a></li>
<li><a target="_blank" href="https://www.martinfowler.com/ieeeSoftware/failFast.pdf">Fail fast</a></li>
</ul>
<p>Now I'm going to introduce you to two functions I wind up bringing into most TypeScript applications I work on, <code>isStrictNever</code> and <code>isWeakNever</code></p>
<h4 id="isstrictnever">isStrictNever</h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> isStrictNever = (x: <span class="hljs-built_in">never</span>): <span class="hljs-function"><span class="hljs-params">never</span> =&gt;</span> {
  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Never case reached with unexpected value <span class="hljs-subst">${x}</span>`</span>);
};
</code></pre>
<p>This function will throw if it is ever called at runtime, and our compile-time checks ensure that this function should never be called.</p>
<h4 id="isweaknever">isWeakNever</h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> isWeakNever = (x: <span class="hljs-built_in">never</span>): <span class="hljs-function"><span class="hljs-params">void</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`Never case reached with unexpected value <span class="hljs-subst">${x}</span> in <span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>().stack}</span>`</span>);
};
</code></pre>
<p>This function will only log at runtime, while still providing compile-time checks ensuring that this function should never be called.</p>
<h3 id="using-isstrictnever-to-fail-fast">Using <code>isStrictNever</code> to fail fast</h3>
<p>Out of the two functions, <code>isStrictNever</code> is preferred in most cases because it helps us fail fast. One place I always use this is if I'm performing any mapping or checks at the database layer. Data consistency at runtime is key, and can easily cause your application to fall into an unpredictable state if the system attempts to continue operating with bad data.</p>
<p>Here's an example:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Article = {
  author: <span class="hljs-built_in">string</span>;
  body: <span class="hljs-built_in">string</span>;
  title: <span class="hljs-built_in">string</span>;
  <span class="hljs-keyword">type</span>: ArticleType;
  publishedAt: <span class="hljs-built_in">Date</span>;
};

<span class="hljs-keyword">type</span> ArticleType = <span class="hljs-string">'STANDARD'</span> | <span class="hljs-string">'SERIES'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> selectLatestArticle = <span class="hljs-keyword">async</span> (): <span class="hljs-built_in">Promise</span>&lt;Article | <span class="hljs-literal">undefined</span>&gt; =&gt; {
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> pool.query(<span class="hljs-string">`
    SELECT id, author, title, body, type_id, published_at 
    FROM articles
    ORDER BY published_at
    LIMIT 1;
  `</span>);
  <span class="hljs-keyword">return</span> res.rows.map(mapArticleRowToArticle).shift();
};

<span class="hljs-keyword">type</span> ArticleRow = {
  author: <span class="hljs-built_in">string</span>;
  body: <span class="hljs-built_in">string</span>;
  title: <span class="hljs-built_in">string</span>;
  type_id: ArticleTypeId;
  published_at: <span class="hljs-built_in">Date</span>;
};

<span class="hljs-keyword">type</span> ArticleTypeId = <span class="hljs-number">1</span> | <span class="hljs-number">2</span>;

<span class="hljs-keyword">const</span> mapArticleRowToArticle = ({published_at, type_id, ...row}: ArticleRow): <span class="hljs-function"><span class="hljs-params">Article</span> =&gt;</span> ({
  ...row,
  publishedAt: published_at
  <span class="hljs-keyword">type</span>: mapArticleTypeIdToTypeString(type_id),
});

<span class="hljs-keyword">const</span> mapArticleTypeIdToTypeString = (typeId: ArticleTypeId): <span class="hljs-function"><span class="hljs-params">ArticleType</span> =&gt;</span> {
  <span class="hljs-keyword">switch</span> (typeId) {
    <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>: {
      <span class="hljs-keyword">return</span> <span class="hljs-string">'STANDARD'</span>;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-number">2</span>: {
      <span class="hljs-keyword">return</span> <span class="hljs-string">'SERIES'</span>;
    }
    <span class="hljs-keyword">default</span>: {
      <span class="hljs-keyword">return</span> isStrictNever(typeId);
    }
  }
};
</code></pre>
<p>Now, if I had a new article type id in the database, but forget to update the code to match, my application will crash whenever it retrieves an article using this new type.</p>
<p>You might think that sounds worse, but it's actually much better. This bug will likely cause end to end tests to fail completely. Because we failed fast, this change is unlikely to make it to production. Even if it does, we can easily track 50x coded HTTP responses in our observability system, we raise alerts when error rates spike, meaning our time to detection and time to recovery will be fast.</p>
<p>If instead we had simply let <code>mapArticleTypeIdToTypeString</code> return <code>undefined</code>, the impact might have been less obvious: it likely would have gone to production, and we would be dealing with disappointed product managers, a product that looks less professional, and have to prioritise bug tickets against new feature work. Given the option, I would much rather fail and fix up front. </p>
<p>Not to mention the fact that latent bugs are vectors for new bugs to interact and cause additional unexpected behaviours, further undermining the reliability of our application.</p>
<p>But a simple <code>isStrictNever</code> can save us all of that pain. So why would we ever use <code>isWeakNever</code>?</p>
<h3 id="using-isweaknever-at-the-intersection-of-third-party-domains">Using <code>isWeakNever</code> at the intersection of third party domains</h3>
<p>Sometimes, we don't fully control the flow of data nor the implementation of types through our system. In these cases, we can use <code>isWeakNever</code> to create type systems that still help us enforce effective compile time checking for the parts of code we do control, while passing through unexpected runtime types from the code we do not control.</p>
<p>I often use this in a redux reducer, for example:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> CountState = {
  count: <span class="hljs-built_in">number</span>;
};

<span class="hljs-keyword">const</span> initialState: CountState = {
  count: <span class="hljs-number">0</span>,
};

<span class="hljs-keyword">type</span> IncrementAction = {
  <span class="hljs-keyword">type</span>: <span class="hljs-string">'INCREMENT'</span>;
}

<span class="hljs-keyword">type</span> DecrementAction = {
  <span class="hljs-keyword">type</span>: <span class="hljs-string">'DECREMENT'</span>;
}

<span class="hljs-keyword">type</span> CountAction = IncrementAction | DecrementAction;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> reducer = (state: CountState = initialState, action: CountAction): <span class="hljs-function"><span class="hljs-params">CountState</span> =&gt;</span> {
  <span class="hljs-keyword">switch</span> (action.type) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">'INCREMENT'</span>: {
      <span class="hljs-keyword">return</span> { count: state.count + <span class="hljs-number">1</span> };
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">'DECREMENT'</span>: {
      <span class="hljs-keyword">return</span> { count: state.count - <span class="hljs-number">1</span> };
    }
    <span class="hljs-keyword">default</span>: {
      isWeakNever(action);
      <span class="hljs-keyword">return</span> state;
    }
  }
};
</code></pre>
<p>Now if I introduce a new count action:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> SquareAction = {
  <span class="hljs-keyword">type</span>: <span class="hljs-string">'SQUARE'</span>;
};

<span class="hljs-keyword">type</span> CountAction = IncrementAction | DecrementAction | SquareAction;
</code></pre>
<p>But neglect to add a new case to the reducer, I will receive a compile time error because <code>action</code> has a possible value in the default case.</p>
<p>This compile time check gives me certainty that my implementation has completeness, while allowing the app to continue running when Redux sends its <code>@@/INIT</code> action, a middleware dispatches an action, or even if we have multiple reducers combined together.</p>
<p>Yes, the type system is lying to us a little, but it is a known, controlled, and measured lie. Logging the uses of <code>isWeakNever</code> gives us visibility into the things that slip through. </p>
<p>While Phryneas (AKA Lenz Weber) levies some valid criticisms towards the discriminated union approach to Redux reducers <a target="_blank" href="https://phryneas.de/redux-typescript-no-discriminating-union">in his article</a>, I believe this approach alleviates his concerns, while making the holes in the type system much more explicit.</p>
<p>This approach also enforces completeness through exhaustive checks, while his approach leaves these checks behind only to focus on payload type hinting.</p>
<p>In my opinion, exhaustive checks AND payload type hinting together are worthwhile, given an explicit indicator that the type system is incomplete, as usage of <code>isWeakNever</code> does.</p>
<p>Have another opinion on the matter? Share your thoughts in the comments below!</p>
]]></content:encoded></item><item><title><![CDATA[Dev Flags: Supercharge your Continuous Deployment by Dropping Database Feature Toggles]]></title><description><![CDATA[Those of us who use  Trunk Based Development  as the foundation of our approach to  Continuous Delivery or continuous Deployment  are familiar with the use of  Feature Toggles  to prevent work in progress changes becoming prematurely available to cus...]]></description><link>https://antman-does-software.com/dev-flags-supercharge-your-continuous-deployment-by-dropping-database-feature-toggles</link><guid isPermaLink="true">https://antman-does-software.com/dev-flags-supercharge-your-continuous-deployment-by-dropping-database-feature-toggles</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Devops]]></category><category><![CDATA[continuous deployment]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sun, 03 Oct 2021 03:39:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1633231787640/ob-16TDb0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Those of us who use  <a target="_blank" href="https://trunkbaseddevelopment.com">Trunk Based Development</a>  as the foundation of our approach to  <a target="_blank" href="https://www.atlassian.com/continuous-delivery/principles/continuous-integration-vs-delivery-vs-deployment">Continuous Delivery or continuous Deployment</a>  are familiar with the use of  <a target="_blank" href="https://martinfowler.com/articles/feature-toggles.html">Feature Toggles</a>  to prevent work in progress changes becoming prematurely available to customers while we develop them. To use the ThoughtWorks vernacular; release toggles.</p>
<p>A mistake I have seen many teams make, and regret, is managing these release toggles from the database. This approach has several issues:</p>
<ol>
<li>Every release must test both paths, as the toggle could change at any time</li>
<li>Developers working on the code base have less visibility into which flags are currently enabled in which environments</li>
<li>Product Managers, developers, and testers often have to coordinate on the removal of feature toggles, this often leads to toggles simply not being removed</li>
<li>Following on from above, old feature toggles often stop being tested. This means every stale feature toggle is a liability that could cause an incident.</li>
<li>A full history of changes is either lost or needs tooling built to support it</li>
</ol>
<p>Sure, many of the above issues could be solved with good tooling and strict processes, aside from #1. However all of these issues can be solved with tools your team has now: Source control &amp; code.</p>
<p>By putting your toggles in code, you get the following advantages:</p>
<ol>
<li>Changes only need to test the active code paths in each release, as they cannot change without another release</li>
<li>Changes to toggles have to go through the full SDLC/release pipeline and thus we can be confident they are working as expected</li>
<li>Developers can clearly see what features are enabled in what environments, who worked on them, and when they were changed</li>
<li>Developers can easily remove toggles once they have finished with them, as part of their feature clean up.</li>
</ol>
<p>Because this approach seriously empowers developers to manage these toggles themselves, I believe this approach needs its own term: <strong>Dev Flags</strong></p>
<p>The advantage of this name is that it is clear who is responsible for them: Developers! This is just part of how we get work done, Product managers don't need to be involved except to confirm they would like a new feature to go live in production. </p>
<p>This clearly delineates the costs involved in maintaining a feature toggle for other purposes, such as A/B Testing, Ops Toggles, and Canaries. By using two seperate systems, we can be clear with product managers, maintaining a new toggle will incur extra overheads on our time, impact our release cycles, and introduce an element of fatigue and complexity over time. This can help us ensure we collaborate on an effective plan to manage the lifecycle of our feature toggles when they <em>are</em> needed, and remove them as soon as they are no longer necessary.</p>
<p>How to implement <strong>Dev Flags</strong>? In projects using TypeScript for frontend and backend, I like to include these in a shared package. My folder structure might be:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633230479302/zEkkePvTj.png" alt="Screen Shot 2021-10-03 at 11.07.31 am.png" /></p>
<p>And the contents of <code>devFlags.ts</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> RuntimeEnvironments = <span class="hljs-string">'dev'</span> | <span class="hljs-string">'stg'</span> | <span class="hljs-string">'prd'</span>;

<span class="hljs-keyword">type</span> DevFlags = {
  useNewProfileScreen: <span class="hljs-built_in">boolean</span>;
  usePhoneCheck: <span class="hljs-built_in">boolean</span>;
};


<span class="hljs-keyword">type</span> DevFlagsEnvironmentMap = Record&lt;Exclude&lt;RuntimeEnvironments, <span class="hljs-string">'stg'</span>&gt;, DevFlags&gt;;

<span class="hljs-keyword">const</span> getEnvironmentDevFlagSet = (env: RuntimeEnvironments): <span class="hljs-function"><span class="hljs-params">DevFlags</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> envs: DevFlagsEnvironmentMap = {
    dev: {
      useNewProfileScreen: <span class="hljs-literal">true</span>,
      usePhoneCheck: <span class="hljs-literal">true</span>,
    },
    prd: {
      useNewProfileScreen: <span class="hljs-literal">true</span>,
      usePhoneCheck: <span class="hljs-literal">false</span>,
    },
  };

  <span class="hljs-keyword">const</span> flagEnv = env === <span class="hljs-string">'stg'</span> ? <span class="hljs-string">'prd'</span> : env;

  <span class="hljs-keyword">return</span> envs[flagEnv];
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> DEV_FLAGS = getEnvironmentDevFlagSet(env.RUNTIME_ENVIRONMENT);
</code></pre>
<p>Why do we exclude staging and instead treat it as prod? Because staging is where we confirm that the current deployment artefact is suitable for release to production. We can't do that if we're executing different code paths in staging and production.</p>
<p>In this one file I can see what features are in development, where they're active, who added which lines, and I can lookup usages to find these flags in the codebase.</p>
<p>As a rule of thumb, you want the number of dev flags in code to be equal to or less than the number of developers working in the code base. This requires some discipline to ensure you remove the flag after releasing the feature. Advantageously, developers are motivated to remember to remove flags because we like to keep our code clean, and we're empowered to do it, because it just another merge, same as we do all day.</p>
<p>Will you try Dev Flags with your team? Does your team do something else? Does it work for you? Let us know in the comments!</p>
<blockquote>
<p>Cover photo: The Twin Jet Nebula, or PN M2-9, is a striking example of a bipolar planetary nebula. Bipolar planetary nebulae are formed when the central object is not a single star, but a binary system, Studies have shown that the nebula’s size increases with time, and measurements of this rate of increase suggest that the stellar outburst that formed the lobes occurred just 1200 years ago.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Sagas & Event Sourcing]]></title><description><![CDATA[By removing the constraint of atomicity for a transaction, we can break up a long lived transaction T into several transactions: \(T_{1},T_{2},T_{3},T_{n}\)
In order to maintain consistency, we must also provide compensatory transactions for failure ...]]></description><link>https://antman-does-software.com/sagas-and-event-sourcing</link><guid isPermaLink="true">https://antman-does-software.com/sagas-and-event-sourcing</guid><category><![CDATA[events]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sun, 05 Sep 2021 12:20:25 GMT</pubDate><content:encoded><![CDATA[<p>By removing the constraint of atomicity for a transaction, we can break up a long lived transaction T into several transactions: \(T_{1},T_{2},T_{3},T_{n}\)</p>
<p>In order to maintain consistency, we must also provide compensatory transactions for failure cases: \(C_{1},C_{2},C_{3},C_{n}\)</p>
<p>In the context of Event Sourcing, this is an argument against achieving idempotent events through stateful payloads instead of delta payloads. To make this clear, consider the following two sets of event sequences:</p>
<p><strong>Stateful Payloads</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>#</td><td>Event Type</td><td>Payload</td><td>Current State</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>Add</td><td><code>{balance: 3}</code></td><td><code>{balance: 3}</code></td></tr>
<tr>
<td>2</td><td>Add</td><td><code>{balance: 5}</code></td><td><code>{balance: 5}</code></td></tr>
<tr>
<td>3</td><td>Add</td><td><code>{balance: 9}</code></td><td><code>{balance: 9}</code></td></tr>
<tr>
<td>4</td><td>Subtract</td><td><code>{balance: 6}</code></td><td><code>{balance: 6}</code></td></tr>
</tbody>
</table>
</div><p>Stateful payloads offer limited idempotency: if I receive the same payload twice, the balance remains the same. However, each event must take a full lock, as each event is dependent on the state of the last event. If two events are written concurrently, they will destructively interfere with each other. Because of this, stateful payloads:</p>
<ul>
<li>Constrain write throughput</li>
<li>Have destructive compensatory events</li>
<li>Are inherently idempotent if sequentially constrained, provided events are never received out of order (e.g. receiving \(E_{3}\) multiple times is non-destructive, 
unless we receive it again after receiving \(E_{4}\) ).</li>
</ul>
<p>The compensatory events are destructive, because if I were to write a compensatory event for \(E_{2}\) at the time \(E_{2}\) occurred it would be <code>{balance: 3}</code>. Applying \(C_{2}\) after \(E_{4}\) results in a state of <code>{balance: 3}</code> instead of <code>{balance: 4}</code>.</p>
<blockquote>
<p>Stateful payloads are not a practical solution to idempotency because where there is more than once delivery, there is out of order delivery. Instead, make the consumer idempotent by keeping a log of processed events.</p>
</blockquote>
<p>Let’s consider the alternative, events that describe what delta to apply to the state to derive the new state.</p>
<p><strong>Delta Payloads with Non Sequential Events</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>#</td><td>Event Type</td><td>Payload</td><td>Current State</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>Add</td><td><code>{amount: 3}</code></td><td><code>{balance: 3}</code></td></tr>
<tr>
<td>2</td><td>Add</td><td><code>{amount: 2}</code></td><td><code>{balance: 5}</code></td></tr>
<tr>
<td>3</td><td>Add</td><td><code>{amount: 4}</code></td><td><code>{balance: 9}</code></td></tr>
<tr>
<td>4</td><td>Subtract</td><td><code>{amount: 3}</code></td><td><code>{balance: 6}</code></td></tr>
</tbody>
</table>
</div><p>As we can see the state at each step is the same. Now we’ve lost the limited idempotency of stateful payloads, but gained:</p>
<ul>
<li>Improved write throughput</li>
<li>Non-destructive compensatory events</li>
</ul>
<p>Lets create a compensatory event for each of these events:</p>
<p><strong>Compensatory Events</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>#</td><td>Event Type</td><td>Payload</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>Subtract</td><td><code>{amount: 3}</code></td></tr>
<tr>
<td>2</td><td>Subtract</td><td><code>{amount: 2}</code></td></tr>
<tr>
<td>3</td><td>Subtract</td><td><code>{amount: 4}</code></td></tr>
<tr>
<td>4</td><td>Add</td><td><code>{amount: 3}</code></td></tr>
</tbody>
</table>
</div><p>If I want to rollback \(E_{2}\) I simply apply \(C_{2}\) to the event stream, resulting in</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>#</td><td>Event Type</td><td>Payload</td><td>Current State</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>Add</td><td><code>{amount: 3}</code></td><td><code>{balance: 3}</code></td></tr>
<tr>
<td>2</td><td>Add</td><td><code>{amount: 2}</code></td><td><code>{balance: 5}</code></td></tr>
<tr>
<td>3</td><td>Add</td><td><code>{amount: 4}</code></td><td><code>{balance: 9}</code></td></tr>
<tr>
<td>4</td><td>Subtract</td><td><code>{amount: 3}</code></td><td><code>{balance: 6}</code></td></tr>
<tr>
<td>5</td><td>Subtract</td><td><code>{amount: 2}</code></td><td><code>{balance: 4}</code></td></tr>
</tbody>
</table>
</div><p>This only applies to non-sequentially constrained event streams. As soon as a sequentially significant event is applied, the entire event stream becomes sequentially constrained, even if the other events are non-sequential on their own.</p>
<p>Here we can see set \(E_{1},E_{2},E_{3},E_{4},C_{2}\) is equivalent to set \(E_{1},E_{3},E_{4}\)</p>
<p>This is because any sequence of \(E_{n}\) is equivalent to any other sequence of \(E_{n}\), and so is its complimentary set of \(C_{n}\) and thus the set of any combination of the members of the two sets combined also be equivalent.</p>
<p><strong>Delta Payloads with Sequential Events</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>#</td><td>Event Type</td><td>Payload</td><td>Current State</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>Add</td><td><code>{amount: 3}</code></td><td><code>{balance: 3}</code></td></tr>
<tr>
<td>2</td><td>Add</td><td><code>{amount: 2}</code></td><td><code>{balance: 5}</code></td></tr>
<tr>
<td>3</td><td>Add</td><td><code>{amount: 4}</code></td><td><code>{balance: 9}</code></td></tr>
<tr>
<td>4</td><td>Divide</td><td><code>{amount: 3}</code></td><td><code>{balance: 3}</code></td></tr>
</tbody>
</table>
</div><p>Consider applying \(C_{2}\) to the above event stream. </p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>#</td><td>Event Type</td><td>Payload</td><td>Current State</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>Add</td><td><code>{amount: 3}</code></td><td><code>{balance: 3}</code></td></tr>
<tr>
<td>2</td><td>Add</td><td><code>{amount: 2}</code></td><td><code>{balance: 5}</code></td></tr>
<tr>
<td>3</td><td>Add</td><td><code>{amount: 4}</code></td><td><code>{balance: 9}</code></td></tr>
<tr>
<td>4</td><td>Divide</td><td><code>{amount: 3}</code></td><td><code>{balance: 3}</code></td></tr>
<tr>
<td>5</td><td>Subtract</td><td><code>{amount: 2}</code></td><td><code>{balance: 1}</code></td></tr>
</tbody>
</table>
</div><p>However, our objective is to wind back the effects of \(E_{2}\), so the desired outcome is</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>#</td><td>Event Type</td><td>Payload</td><td>Current State</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>Add</td><td><code>{amount: 3}</code></td><td><code>{balance: 3}</code></td></tr>
<tr>
<td>3</td><td>Add</td><td><code>{amount: 4}</code></td><td><code>{balance: 7}</code></td></tr>
<tr>
<td>4</td><td>Divide</td><td><code>{amount: 3}</code></td><td><code>{balance: 2.3}</code></td></tr>
</tbody>
</table>
</div><p>In this case, we cannot guarantee a compensatory event will be non-destructive unless we can guarantee no sequential events have been inserted between \(E_{2}\) and \(C_{2}\). With sequential events in the mix, we can only make that guarantee if we lock the event stream, constraining write throughput and preventing entries between \(E_{2}\) and \(C_{2}\) to regain atomicity. </p>
<p>This leads us to the following assertions:</p>
<ul>
<li>Compensatory events are non-destructive if both set \(E_{n}\) and set \(C_{n}\) are non-sequential.</li>
<li>\(E_{n}\) is non-sequential if \(E_{1},E_{2},E_{3}\) is equivalent to \(E_{2},E_{1},E_{3}\) and any other combination.</li>
<li>A single sequential event in an event stream breaks the non-destructive compensation guarantee.</li>
</ul>
<p><strong>Caveats</strong></p>
<ul>
<li>Event stream sequentiality is derived from the combination of reducer and events. 
 This means you could create a second reducer that processes events such that they are no longer non-sequential.<ul>
<li>If \(R_{1}(E_{n})\) is non-sequential, you cannot guarantee \(R_{2}(E_{n})\) is also sequential.</li>
</ul>
</li>
</ul>
<p><strong>Practical Applications</strong></p>
<p>When designing event sourced systems with long lived transactions modelled as sagas, you gain non-destructive compensatory events by ensuring your events are non-sequential. You only need to maintain this non-sequentiality constraint for events in the same stream processed by the same reducer. This means the projection derived from \(R_{1}(E_{n})\) is unaffected by sequential events in stream \(E_{b}\), ergo \(R_{2}(E_{n+b})\) cannot non-destructively handle compensatory events in stream \(E_{n}\) while \(R_{1}(E_{n})\). </p>
<p>In a practical sense, reverting a sales order might re-apply deducted funds from a users account — \(R_{1}(E_{n})\) is non-destructive — but it won’t put an item back into inventory if it has already shipped — \(R_{2}(E_{n+b})\) <em>is</em> destructive.</p>
<p>The key takeaway here is to be mindful of your events — are they sequential? If so, is it because there is some stateful information in the event payload that should be refactored into a delta? If it cannot be turned into a delta, you will need to consider the business implications of your compensatory event, i.e. booking a return parcel for a customer, dispatching an email, etc. It often becomes a matter of invoking a compensatory command or saga within the destructively compensated domain.</p>
]]></content:encoded></item><item><title><![CDATA[Functional Singletons in TypeScript With Real Use Cases]]></title><description><![CDATA[Singletons are commonly used in Object Oriented Programming when we want to enforce that there is only ever a single instance of a class. This might be because we are trying to encapsulate some global state between processes.
In this article, I will ...]]></description><link>https://antman-does-software.com/functional-singletons-in-typescript-with-real-use-cases</link><guid isPermaLink="true">https://antman-does-software.com/functional-singletons-in-typescript-with-real-use-cases</guid><category><![CDATA[Functional Programming]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[design patterns]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Sun, 22 Aug 2021 06:28:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1629613410419/3GCoCWBB5.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Singletons are commonly used in Object Oriented Programming when we want to enforce that there is only ever a single instance of a class. This might be because we are trying to encapsulate some global state between processes.</p>
<p>In this article, I will use the queue from my <a target="_blank" href="https://github.com/Antman261/es-reduxed">es-reduxed</a> package. The purpose of this queue is to:</p>
<ul>
<li>Store a list of unprocessed event ids</li>
<li>Maintain the correct order of events</li>
<li>Ensure an event is only processed once</li>
</ul>
<p>These assurances must be made because there is no guarantee that the events will be sent from the subscription only once and in order. While the system is processing one event, it may receive several more.</p>
<p>As you can guess, if we were to wind up with two instances of the event queue, we could no longer guarantee order and single processing constraints.</p>
<p>Let's take a look at the queue itself, starting with the types:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> Queue = {
  enqueue: <span class="hljs-function">(<span class="hljs-params">id: <span class="hljs-built_in">number</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  registerPromise: <span class="hljs-function">(<span class="hljs-params">id: <span class="hljs-built_in">number</span>, resolver: PromiseResolver</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> PromiseResolver = <span class="hljs-function">(<span class="hljs-params">value: Store&lt;<span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>&gt;</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
</code></pre>
<p>So we can see a <code>Queue</code> is just an object with two properties:</p>
<ul>
<li><code>enqueue</code>: a function taking a <code>number</code> and returning <code>void</code></li>
<li><code>registerPromise</code>: a function taking a number and a function</li>
</ul>
<p>The <code>registerPromise</code> function is just associating a resolve function from a Promise with an event id so that the queue can resolve the promise with the given redux state when it finishes processing that event. </p>
<p>Let's take a look at the queue implementation:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">/**
 * This queue system uses a recursive loop and a primitive state machine to
 * ensure that events are dispatched to redux in exactly the order they were
 * received.
 */</span>
<span class="hljs-keyword">const</span> startQueue = &lt;T <span class="hljs-keyword">extends</span> EventBase&gt;<span class="hljs-function">(<span class="hljs-params">
  reduxStore: Store&lt;<span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>&gt;,
  eventsRepo: EventsRepo&lt;T&gt;
</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> queue: <span class="hljs-built_in">number</span>[] = [];
  <span class="hljs-keyword">const</span> dedupeSet = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>&lt;<span class="hljs-built_in">number</span>&gt;();
  <span class="hljs-keyword">const</span> promiseMap = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">number</span>, PromiseResolver&gt;();
  <span class="hljs-keyword">let</span> state: <span class="hljs-string">'READY'</span> | <span class="hljs-string">'PROCESSING'</span> = <span class="hljs-string">'READY'</span>;

  <span class="hljs-keyword">const</span> processEvent = <span class="hljs-function">(<span class="hljs-params">event: EventBase</span>) =&gt;</span> {
    reduxStore.dispatch(event);
    <span class="hljs-keyword">if</span> (event.id === <span class="hljs-literal">undefined</span>) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Malformed event is missing id: <span class="hljs-subst">${event}</span>`</span>);
    }
    <span class="hljs-keyword">const</span> resolver = promiseMap.get(event.id);
    <span class="hljs-keyword">if</span> (resolver) {
      resolver(reduxStore.getState());
      promiseMap.delete(event.id);
    }
  };

  <span class="hljs-keyword">const</span> processQueue = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">if</span> (state === <span class="hljs-string">'READY'</span>) {
      queue.sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> a - b);
      <span class="hljs-keyword">const</span> eventId = queue.shift(); <span class="hljs-comment">// So we only process if something was in the queue</span>
      <span class="hljs-keyword">if</span> (eventId) {
        state = <span class="hljs-string">'PROCESSING'</span>;
        <span class="hljs-keyword">if</span> (queue.length) {
          <span class="hljs-comment">// More than one event in queue, so do bulk processing</span>
          <span class="hljs-keyword">const</span> lastEventIndex = queue.length - <span class="hljs-number">1</span>; <span class="hljs-comment">// Save queue length in-case it changes during the await</span>
          <span class="hljs-keyword">const</span> lastEventId = queue[lastEventIndex];
          <span class="hljs-keyword">const</span> events = <span class="hljs-keyword">await</span> eventsRepo.getEventRange(eventId, lastEventId);
          events.forEach(processEvent);
          queue.splice(<span class="hljs-number">0</span>, lastEventIndex + <span class="hljs-number">1</span>);
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">const</span> [event] = <span class="hljs-keyword">await</span> eventsRepo.getEvents(eventId - <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
          processEvent(event);
        }
        state = <span class="hljs-string">'READY'</span>;
        processQueue();
      }
    }
  };

  <span class="hljs-keyword">return</span> {
    enqueue: <span class="hljs-function">(<span class="hljs-params">id: <span class="hljs-built_in">number</span> | <span class="hljs-built_in">string</span></span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> idCoerced = <span class="hljs-keyword">typeof</span> id === <span class="hljs-string">'string'</span> ? <span class="hljs-built_in">parseInt</span>(id, <span class="hljs-number">10</span>) : id;
      <span class="hljs-keyword">if</span> (!dedupeSet.has(idCoerced)) {
        dedupeSet.add(idCoerced);
        queue.push(idCoerced);
        processQueue();
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">`Out of order event: [<span class="hljs-subst">${idCoerced}</span>]`</span>);
      }
    },
    registerPromise: <span class="hljs-function">(<span class="hljs-params">id: <span class="hljs-built_in">number</span>, resolve: PromiseResolver</span>) =&gt;</span> {
      promiseMap.set(id, resolve);
    },
  };
};
</code></pre>
<p>Okay, let's break this down:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> startQueue = &lt;T <span class="hljs-keyword">extends</span> EventBase&gt;<span class="hljs-function">(<span class="hljs-params">
  reduxStore: Store&lt;<span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>&gt;,
  eventsRepo: EventsRepo&lt;T&gt;
</span>) =&gt;</span> {
</code></pre>
<p>The first thing to notice here is that we do not export this function, <code>startQueue</code> is available only inside the <code>queue.ts</code> module. This is a critical point we will come back to later.</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">const</span> processEvent = <span class="hljs-function">(<span class="hljs-params">event: EventBase</span>) =&gt;</span> {
    reduxStore.dispatch(event);
    <span class="hljs-keyword">if</span> (event.id === <span class="hljs-literal">undefined</span>) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Malformed event is missing id: <span class="hljs-subst">${event}</span>`</span>);
    }
    <span class="hljs-keyword">const</span> resolver = promiseMap.get(event.id);
    <span class="hljs-keyword">if</span> (resolver) {
      resolver(reduxStore.getState());
      promiseMap.delete(event.id);
    }
  };
</code></pre>
<p>This function is defined inside <code>startQueue</code>, so it is only available within the <code>startQueue</code> function. This is similar to a private method. However, it has access to all variables included in its closure. In this case, we are making use of <code>promiseMap</code> and <code>reduxStore</code>. This is similar to private properties in a class, but we use closures to make them inaccessible outside this context.</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">const</span> processQueue = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">if</span> (state === <span class="hljs-string">'READY'</span>) {
      queue.sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> a - b);
      <span class="hljs-keyword">const</span> eventId = queue.shift(); <span class="hljs-comment">// So we only process if something was in the queue</span>
      <span class="hljs-keyword">if</span> (eventId) {
        state = <span class="hljs-string">'PROCESSING'</span>;
        <span class="hljs-keyword">if</span> (queue.length) {
          <span class="hljs-comment">// More than one event in queue, so do bulk processing</span>
          <span class="hljs-keyword">const</span> lastEventIndex = queue.length - <span class="hljs-number">1</span>; <span class="hljs-comment">// Save queue length in-case it changes during the await</span>
          <span class="hljs-keyword">const</span> lastEventId = queue[lastEventIndex];
          <span class="hljs-keyword">const</span> events = <span class="hljs-keyword">await</span> eventsRepo.getEventRange(eventId, lastEventId);
          events.forEach(processEvent);
          queue.splice(<span class="hljs-number">0</span>, lastEventIndex + <span class="hljs-number">1</span>);
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">const</span> [event] = <span class="hljs-keyword">await</span> eventsRepo.getEvents(eventId - <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
          processEvent(event);
        }
        state = <span class="hljs-string">'READY'</span>;
        processQueue();
      }
    }
  };
</code></pre>
<p>Here we continually process the queue in a recursive loop, as long as there are events remaining and the queue is not already processing events. This ensures we only process events once. Because this function is <code>async</code> (it returns a promise), and because it will always call <code>await</code> before it recursively calls <code>processQueue</code> again, this function will not lock the event loop.</p>
<p>It's also important to realise that there might be calls to <code>enqueue</code> during the <code>await</code> step. This would grow the queue, but not be included in the call to <code>getEventRange</code>, so this is why we save the last event index before we <code>await</code>; otherwise, we could accidentally splice out events we hadn't processed yet.</p>
<p>Now that we understand the queue's "private methods" and properties, let's take a look at the "public methods" in the return statement:</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">return</span> {
    enqueue: <span class="hljs-function">(<span class="hljs-params">id: <span class="hljs-built_in">number</span> | <span class="hljs-built_in">string</span></span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> idCoerced = <span class="hljs-keyword">typeof</span> id === <span class="hljs-string">'string'</span> ? <span class="hljs-built_in">parseInt</span>(id, <span class="hljs-number">10</span>) : id;
      <span class="hljs-keyword">if</span> (!dedupeSet.has(idCoerced)) {
        dedupeSet.add(idCoerced);
        queue.push(idCoerced);
        processQueue();
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">`Out of order event: [<span class="hljs-subst">${idCoerced}</span>]`</span>);
      }
    },
    registerPromise: <span class="hljs-function">(<span class="hljs-params">id: <span class="hljs-built_in">number</span>, resolve: PromiseResolver</span>) =&gt;</span> {
      promiseMap.set(id, resolve);
    },
  };
</code></pre>
<p>Aha! So when we enqueue an event, we call <code>processQueue</code> if it is an event we haven't seen before. Remember the state checks in <code>processQueue</code>? We can safely call this function here because it won't do anything if there is already a process running, and it will eventually get to our enqueued event through the recursive loop.</p>
<p>Meanwhile, <code>registerPromise</code> maps a promise to the event id, which will be used later. In this implementation, we only allow resolving one promise per event processed.</p>
<p>Let's get to the meat of this article, the function that instantiates or retrieves this queue as a singleton:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getQueue = (<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">let</span> instance: Queue;
  <span class="hljs-keyword">return</span> &lt;T <span class="hljs-keyword">extends</span> EventBase&gt;<span class="hljs-function">(<span class="hljs-params">
    reduxStore: Store&lt;<span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>&gt;,
    eventsRepo: EventsRepo&lt;T&gt;
  </span>) =&gt;</span> {
    instance =
      instance === <span class="hljs-literal">undefined</span> ? startQueue&lt;T&gt;(reduxStore, eventsRepo) : instance;
    <span class="hljs-keyword">return</span> instance;
  };
})();
</code></pre>
<p>First, take note of the fact that this is the only run-time export from <code>queue.ts</code>. You can <em>only</em> retrieve a queue by calling <code>getQueue</code>, but what's actually happening here?</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getQueue = (<span class="hljs-function">() =&gt;</span> {
 <span class="hljs-comment">// Trimmed</span>
})();
</code></pre>
<p>Above is an immediately invoked function expression. We are defining a function and then calling it. The result of this function is then assigned to the variable <code>getQueue</code>. Well, <code>getQueue</code> is a function -- we can tell because the first word is a verb -- so this immediately invoked function expression needs to return a function.</p>
<pre><code class="lang-typescript">(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">let</span> instance: Queue;
  <span class="hljs-keyword">return</span> <span class="hljs-comment">// Trimmed</span>
})();
</code></pre>
<p>Before it returns, we declare a mutable variable called <code>instance</code> of the type <code>Queue</code> but do not define a value for it, which means it will be undefined.</p>
<pre><code class="lang-typescript">(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">let</span> instance: Queue;
  <span class="hljs-keyword">return</span> &lt;T <span class="hljs-keyword">extends</span> EventBase&gt;<span class="hljs-function">(<span class="hljs-params">
    reduxStore: Store&lt;<span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>&gt;,
    eventsRepo: EventsRepo&lt;T&gt;
  </span>) =&gt;</span> {
    <span class="hljs-comment">// Trimmed</span>
  };
})();
</code></pre>
<p>Aha! So after we declare our <code>instance</code> variable for storing a queue, we define a function for our immediately invoked function expression to return. This function signature should look familiar -- it is identical to <code>startQueue</code>'s function signature.</p>
<pre><code class="lang-typescript">(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">let</span> instance: Queue;
  <span class="hljs-keyword">return</span> &lt;T <span class="hljs-keyword">extends</span> EventBase&gt;<span class="hljs-function">(<span class="hljs-params">
    reduxStore: Store&lt;<span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>&gt;,
    eventsRepo: EventsRepo&lt;T&gt;
  </span>) =&gt;</span> {
    instance =
      instance === <span class="hljs-literal">undefined</span> ? startQueue&lt;T&gt;(reduxStore, eventsRepo) : instance;
    <span class="hljs-keyword">return</span> instance;
  };
})();
</code></pre>
<p>The final step: Inside the function returned to <code>getQueue</code> by our immediately invoked function expression, we will: If <code>instance</code> is undefined, call <code>startQueue</code>, passing in our generic type parameter, <code>reduxStore</code>, and <code>eventsRepo</code>, OR, in the case that it is defined, we simply leave <code>instance</code> as <code>instance</code>. Then we return <code>instance</code>.</p>
<p>This means that <code>instance</code> is stored in the closure scope of <code>getQueue</code>. It is no longer accessible anywhere in javascript except by <code>getQueue</code> itself. We can be confident that <code>getQueue</code> will only ever return a single queue instance. The first time it instantiates the queue, and subsequent calls return it.</p>
<p>You can test this fact by asserting:</p>
<pre><code class="lang-typescript">expect(getQueue()).to.equal(getQueue());
</code></pre>
<p>This will return true because both calls return a reference to the same object!</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">console</span>.log(getQueue() === getQueue()); <span class="hljs-comment">// true</span>
</code></pre>
<p>Remember object equality in javascript compares references.</p>
<p>There's a massive caveat in this implementation; can you spot it? <em>We only use the parameters the first time <code>getQueue</code> is called!</em> This means that if I try to start one queue for one store and another queue for another store, it will simply ignore the second store and return my queue for the first store.</p>
<p>In this way, this pattern is <em>not</em> memoization. If we were using memoization, we would be able to create a singleton per combination of <code>reduxStore</code> and <code>eventsRepo</code>. However, this makes it harder to enforce a singleton pattern. What defines whether <code>reduxStore</code> and <code>eventsRepo</code> are the same as the last call? Would we compare object equality? Deeply nested properties?</p>
<p>For example, if I were to use a proxy-based memoization implementation such as <a target="_blank" href="https://www.npmjs.com/package/proxy-memoize">proxy-memoize</a> then changes to properties of, or child properties of <code>reduxStore</code> and <code>eventsRepo</code> would cause calls to <code>getQueue</code> to return a new queue instance! Uh oh!</p>
<p>I like to think of this pattern as <em>brutal memoize</em>. You get to provide your parameters <em>once</em>, and after that, we ignore them.</p>
<p>Will you be using this functional singleton pattern in your code? Do you have a different approach? Let me know what you think in the comments!</p>
<blockquote>
<p>Photo by <a target="_blank" href="https://unsplash.com/@lazycreekimages?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Michael Dziedzic</a> on Unsplash</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Why You Will Never Write Another "Down" Migration]]></title><description><![CDATA[If you've used frameworks like Rails, Django, or even Hasura, you are probably familiar with the concept of "up" migrations and "down" migrations. These might also be called forwards and backwards, or in flyway they are called "undo" migrations.
For ...]]></description><link>https://antman-does-software.com/why-you-will-never-write-another-down-migration</link><guid isPermaLink="true">https://antman-does-software.com/why-you-will-never-write-another-down-migration</guid><category><![CDATA[SQL]]></category><category><![CDATA[Databases]]></category><category><![CDATA[Django]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[PostgreSQL]]></category><dc:creator><![CDATA[Anthony Manning-Franklin]]></dc:creator><pubDate>Thu, 12 Aug 2021 13:56:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1628776444171/FM66_qHiG.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you've used frameworks like Rails, Django, or even Hasura, you are probably familiar with the concept of "up" migrations and "down" migrations. These might also be called forwards and backwards, or in flyway they are called "undo" migrations.</p>
<p>For the unfamiliar, the migrations we are talking about here are a set of schema changes for a relational database such as Postgres, MySQL, etc. Sometimes these are generated by an ORM based on models, and sometimes they are written by developers. They're almost always (and should be) committed to version control systems, and must be executed in a particular order.</p>
<p>Our "up" migrations might create some tables, alter some columns, set constraints, and so forth. This is normally a straight-forward affair. Developers do this day in and day out, we test these on our local before committing them, and if we're going to run alterations on existing tables, we usually test them out on prod-like data to measure impact. We're confident about up migrations.</p>
<p>My case against down migrations starts rather predictably; when do you run them? Hardly ever. How confident are you that every down migration in source control was tested before it was executed? Was it tested <em>after</em> prod-like data was inserted into the table?</p>
<p>I know the answer to both those questions is "Oh s#%T!" or an amused but panicked chuckle to yourself. The fact is that when it comes time to fix a schema problem in production, those down migrations you've been saving for a rainy day? There is <em>NEVER</em> a situation where they are the right solution.</p>
<p>Teams sometimes laugh nervously amongst themselves that their disaster recovery plans are an entirely unknown quantity because they've never tested them. Down migrations are like hundreds of untested disaster recovery plans, <em>only much much worse.</em></p>
<p>Putting down migrations in source control, and then deploying them to production, is like enjoying a TV dinner with unexploded ordnance on the coffee table. At some point, someone is going to mistakenly run "down" in prod thinking it was dev/local/etc. At least you've practiced your disaster recovery though, right? Hey how often do your automated backups run and when did you last check them? Go, I'll be here when you get back!</p>
<p>...Oh you're back? Are you okay? You look stressed! Anyway, even if you do successfully run a down migration at some point and it proves useful... now what? Your migration history no longer makes sense. The fact that X, Y, or Z was run in production to change the schema in some particular way and unpick some mess, is lost to history.</p>
<p>I propose you forget all about down migrations. Delete them all from source control right now, I promise you no one will miss them. </p>
<p>What happens if you do make a change you want to rollback in production? Ah yes, you carefully figure out how to roll forward! You write a new "up" migration to clean up your mistake. That's right, you write your drop table or alter column statements in an up migration.  You prepare a run sheet, you test, and you go through your normal SDLC for deploying schema changes (You do run migrations via CD right?) Now your changes are in source control. They're part of history, as they should be, because it happened, and all future migrations will safely run on top of that history.</p>
<p>So what do you think? Are you going to delete your down migrations from source control? I'd love to hear about it if you do! Come back and tell us how it went in the comments!</p>
<blockquote>
<p>Photo by Avery Nielsen-Webb from Pexels</p>
</blockquote>
]]></content:encoded></item></channel></rss>