<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Packt Deep Engineering: Practical Deep-Dives]]></title><description><![CDATA[Hands‑on explorations of tools, patterns, and practices shaping modern software]]></description><link>https://deepengineering.substack.com/s/practical-deep-dives</link><image><url>https://substackcdn.com/image/fetch/$s_!H5BJ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png</url><title>Packt Deep Engineering: Practical Deep-Dives</title><link>https://deepengineering.substack.com/s/practical-deep-dives</link></image><generator>Substack</generator><lastBuildDate>Tue, 16 Jun 2026 22:40:54 GMT</lastBuildDate><atom:link href="https://deepengineering.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Packt]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[deepengineering@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[deepengineering@substack.com]]></itunes:email><itunes:name><![CDATA[Packt]]></itunes:name></itunes:owner><itunes:author><![CDATA[Packt]]></itunes:author><googleplay:owner><![CDATA[deepengineering@substack.com]]></googleplay:owner><googleplay:email><![CDATA[deepengineering@substack.com]]></googleplay:email><googleplay:author><![CDATA[Packt]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[From NIC to P99: Engineering Low-Latency C++ Trading Systems in 2026]]></title><description><![CDATA[A practical look at the hardware, OS, and code-level optimizations you need to stop burning your latency budget.]]></description><link>https://deepengineering.substack.com/p/from-nic-to-p99-engineering-low-latency</link><guid isPermaLink="false">https://deepengineering.substack.com/p/from-nic-to-p99-engineering-low-latency</guid><dc:creator><![CDATA[Shreyans]]></dc:creator><pubDate>Wed, 04 Mar 2026 08:19:17 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b449d007-c2e0-4447-a359-fffc7681e42b_1792x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Trading systems have been <a href="https://www.globaltrading.net/the-need-for-speed/">evolving for decades</a>, moving from manual execution based on charts and indicators to automated strategies that fire within tens of nanoseconds using FPGA and ASIC hardware. Both approaches still coexist today, but most advanced and systematic trading has moved into the sub-100 microsecond range. In this article I want to focus on the hardware and software aspects you need to understand in order to build a trading system with p95 wire-to-wire latency under 50 microseconds.</p><p>Traditional technology stacks like <a href="https://www.linkedin.com/pulse/comparing-rust-c-python-java-go-typescriptnodejs-hft-trading-souza-nxlkf/">Java, JavaScript, TypeScript, and C# offer a lot of flexibility</a> and scalability for large-scale applications, but their inherently nondeterministic nature makes it very difficult to get below a p50 latency of 500 microseconds. While there are ways to optimize garbage collection and other Java components for latency, most standard Java libraries do not focus on low-level concepts like aligning data to cache lines or reducing cache line misses. C and C++ applications optimize for a specific platform at the cost of portability, but that trade-off is exactly what gives you access to manual memory management and the ability to avoid multithreading pitfalls like false sharing. When the goal is to reduce latency, you need lower-level languages like C, C++, or Rust on critical paths. I will not cover pure hardware implementations like FPGAs or ASICs here since they are not required for our 50-microsecond latency target.</p><h2>Important Definitions</h2><p>Before diving into the architecture and hardware optimizations, it will be helpful to establish exactly how performance is measured in a low-latency environment.</p><h3>Wire-to-wire latency</h3><p>Wire-to-wire latency is measured from the moment a market data packet is received on the NIC from the exchange to the moment an order is sent back to the exchange on the same NIC. You compute it as order send timestamp minus market data receive timestamp. The most precise measurements require NIC cards with hardware timestamping enabled, so the NIC stamps every inbound and outbound packet with a hardware timestamp that you can later use to analyze the latency graph. Exanic cards provide APIs for retrieving these hardware timestamps.</p><p><strong>Assumptions About the System</strong></p><p>To hit the latency target of under 50 microseconds, you need to optimize both hardware and server setup alongside your software, and the following assumptions underpin everything in this article.</p><ul><li><p><strong>Colocated host:</strong> Most stock exchanges in traditional finance provide colocation services where your servers sit physically adjacent to the exchange&#8217;s servers, removing extra network jitter from the equation.</p></li><li><p><strong>UDP market data access:</strong> Most stock exchanges distribute market data via UDP multicast to all subscribed parties, and this is the fastest way to consume and react to it.</p></li><li><p><strong>Kernel bypass NIC:</strong> The system runs on NIC cards enabled for kernel bypass and tuned for low-latency operation.</p></li><li><p><strong>Single venue:</strong> These systems are single-venue, meaning at any point they process market data to generate orders for one stock exchange only. For multi-venue setups, the assumption is one colocated host per venue with no shared critical-path processing.</p></li></ul><h3>Workload Model</h3><p>Most of the trading systems follow this model of data processing in abstract terms:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_1_W!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3e071ec-b507-4278-a874-052d6e5cd7c1_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_1_W!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3e071ec-b507-4278-a874-052d6e5cd7c1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!_1_W!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3e071ec-b507-4278-a874-052d6e5cd7c1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!_1_W!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3e071ec-b507-4278-a874-052d6e5cd7c1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!_1_W!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3e071ec-b507-4278-a874-052d6e5cd7c1_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_1_W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3e071ec-b507-4278-a874-052d6e5cd7c1_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d3e071ec-b507-4278-a874-052d6e5cd7c1_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_1_W!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3e071ec-b507-4278-a874-052d6e5cd7c1_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!_1_W!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3e071ec-b507-4278-a874-052d6e5cd7c1_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!_1_W!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3e071ec-b507-4278-a874-052d6e5cd7c1_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!_1_W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3e071ec-b507-4278-a874-052d6e5cd7c1_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Every strategy engine takes market data from the exchange as its primary input and produces orders sent to the exchange as its primary output, moving through multiple stages as described below. Exchange responses are a secondary input to the engine but are not on the hot path for latency calculations.</p><h3>Market Data Ingestion</h3><p>The first layer is responsible for ingesting the market data (raw or normalized) from the network card. This layer routinely processes terabytes of data per day and can exceed a petabyte on busy days at venues like <a href="https://finance.yahoo.com/news/cme-group-international-average-daily-080000527.html">CME</a> or <a href="https://www.eurex.com/ex-en/data/statistics/monthly-statistics">Eurex</a>. On an average day at CME you can expect 20 to 30 million trades and 100 to 150 million open interest events in a single segment. Processing a full L1 feed for one segment means receiving traffic in the range of a few terabytes to hundreds of terabytes per day, so both software and hardware need to be extremely optimized.</p><h3>Filtering and Checks</h3><p>Filtering and checks come next, cleaning up incoming data and validating it before it reaches downstream stages. This layer handles packet drops, invalid market data, trading phase transitions like moving from an active session to a halted one, and anomalous price changes that might indicate a software bug. When something goes wrong at this stage, the right response is often to instruct strategies to stop rather than pass bad data forward.</p><h3>Pricing Engine</h3><p>The pricing engine receives valid and normalized market data and can range from a simple no-op or currency conversion all the way to a full ML-powered model. Lightweight pricing fits comfortably on the critical path. Complex ML models that require large amounts of memory and processing do not, because they would blow the latency budget. In practice, ML-powered engines in low-latency applications split into two processes: time-consuming training and inference model preparation happen off the critical path, and a lightweight inference step runs on the critical path using small linearized models fed configuration parameters from the heavier ML output. This keeps the heavy lifting off the hot path while still giving the strategy access to model-driven signals. Colocation also constrains available compute, so you want to reserve it for work that directly impacts critical path performance.</p><h3>Strategy Evaluation</h3><p>Strategy evaluation compares transformed or theoretical prices against real prices and fires an order when it identifies an opportunity. Strategies generally fall into three categories.</p><ul><li><p><strong>Maker:</strong> Maker strategies keep quotes on both the buy and sell sides to provide liquidity and take advantage of the bid-ask spread. Exchanges often provide incentives to HFT firms that add liquidity this way.</p></li><li><p><strong>Taker:</strong> Taker strategies identify mispricing and fire IOC or FOK orders for immediate execution.</p></li><li><p><strong>Hedging:</strong> Hedging strategies reduce risk or book profit by executing the opposite side of an existing position.</p></li></ul><h3><strong>Order Aggregation and Routing (OMS)</strong></h3><p>Order aggregation and routing (OMS) can be combined with strategy evaluation in simple setups, but should be separated as complexity grows. It serves three purposes: aggregating orders from multiple strategies to reduce the number of orders sent to the exchange, managing exchange responses and passing normalized results back to the strategy, and handling the routing logic that grows more complex as your strategy count increases.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://deepengineering.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Deep Engineering! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h2>Latency Map: Where the Latency Lies</h2><p>Now that the high-level components are clear, it is worth mapping out where the latency actually sits so you can allocate your budget with precision. Beyond the business logic itself, your latency profile depends on CPU model and generation, NIC type and model, operating system, software architecture, and business logic complexity.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1YAa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b2ef2c-ba7d-4409-9393-d96d2b5abbee_859x293.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1YAa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b2ef2c-ba7d-4409-9393-d96d2b5abbee_859x293.png 424w, https://substackcdn.com/image/fetch/$s_!1YAa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b2ef2c-ba7d-4409-9393-d96d2b5abbee_859x293.png 848w, https://substackcdn.com/image/fetch/$s_!1YAa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b2ef2c-ba7d-4409-9393-d96d2b5abbee_859x293.png 1272w, https://substackcdn.com/image/fetch/$s_!1YAa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b2ef2c-ba7d-4409-9393-d96d2b5abbee_859x293.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1YAa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b2ef2c-ba7d-4409-9393-d96d2b5abbee_859x293.png" width="724" height="246.9522700814901" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f2b2ef2c-ba7d-4409-9393-d96d2b5abbee_859x293.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:293,&quot;width&quot;:859,&quot;resizeWidth&quot;:724,&quot;bytes&quot;:46376,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/189117004?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b2ef2c-ba7d-4409-9393-d96d2b5abbee_859x293.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1YAa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b2ef2c-ba7d-4409-9393-d96d2b5abbee_859x293.png 424w, https://substackcdn.com/image/fetch/$s_!1YAa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b2ef2c-ba7d-4409-9393-d96d2b5abbee_859x293.png 848w, https://substackcdn.com/image/fetch/$s_!1YAa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b2ef2c-ba7d-4409-9393-d96d2b5abbee_859x293.png 1272w, https://substackcdn.com/image/fetch/$s_!1YAa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b2ef2c-ba7d-4409-9393-d96d2b5abbee_859x293.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Beyond the business logic stages, several factors contribute latency that stays invisible if you only look at application code. These factors come from hardware, OS and kernel processing, network behavior, and I/O, and they play an outsized role in ultra-low-latency systems. Even if they occur infrequently, they introduce jitter and latency spikes that directly hurt your p99. Although many of these latency sources depend on a multitude of factors, you can work with approximate cost ranges that provide a useful benchmark, and the table below gives those ranges for the most common sources.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_K9p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4620df02-490f-4432-9b8f-d5382030692a_610x269.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_K9p!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4620df02-490f-4432-9b8f-d5382030692a_610x269.png 424w, https://substackcdn.com/image/fetch/$s_!_K9p!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4620df02-490f-4432-9b8f-d5382030692a_610x269.png 848w, https://substackcdn.com/image/fetch/$s_!_K9p!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4620df02-490f-4432-9b8f-d5382030692a_610x269.png 1272w, https://substackcdn.com/image/fetch/$s_!_K9p!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4620df02-490f-4432-9b8f-d5382030692a_610x269.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_K9p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4620df02-490f-4432-9b8f-d5382030692a_610x269.png" width="594" height="261.94426229508196" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4620df02-490f-4432-9b8f-d5382030692a_610x269.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:269,&quot;width&quot;:610,&quot;resizeWidth&quot;:594,&quot;bytes&quot;:38686,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/189117004?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4620df02-490f-4432-9b8f-d5382030692a_610x269.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_K9p!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4620df02-490f-4432-9b8f-d5382030692a_610x269.png 424w, https://substackcdn.com/image/fetch/$s_!_K9p!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4620df02-490f-4432-9b8f-d5382030692a_610x269.png 848w, https://substackcdn.com/image/fetch/$s_!_K9p!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4620df02-490f-4432-9b8f-d5382030692a_610x269.png 1272w, https://substackcdn.com/image/fetch/$s_!_K9p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4620df02-490f-4432-9b8f-d5382030692a_610x269.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Engineering Pillars</strong></h2><p>Addressing these latency sources requires a set of engineering decisions baked into the application design itself, not patched in afterwards. The pillars below cover the most impactful areas.</p><h3><strong>Kernel Bypass</strong></h3><p>Traditionally, any application reading from the network goes through the kernel as an intermediary, which means a system call copies data from the NIC into kernel space before the application can access it by copying from kernel space into its own buffers. That double-copy plus the system call overhead adds up quickly because control needs to transfer to and from the kernel on every read, and the way to avoid it is to access the NIC directly through its driver. DPDK polling mode is one of the best approaches for this and can save roughly 20 to 45 microseconds when tuned correctly for market data processing.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MsWe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa77aece3-a437-4bd2-bd1a-e5948d723152_535x119.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MsWe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa77aece3-a437-4bd2-bd1a-e5948d723152_535x119.png 424w, https://substackcdn.com/image/fetch/$s_!MsWe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa77aece3-a437-4bd2-bd1a-e5948d723152_535x119.png 848w, https://substackcdn.com/image/fetch/$s_!MsWe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa77aece3-a437-4bd2-bd1a-e5948d723152_535x119.png 1272w, https://substackcdn.com/image/fetch/$s_!MsWe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa77aece3-a437-4bd2-bd1a-e5948d723152_535x119.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MsWe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa77aece3-a437-4bd2-bd1a-e5948d723152_535x119.png" width="622" height="138.35140186915888" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a77aece3-a437-4bd2-bd1a-e5948d723152_535x119.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:119,&quot;width&quot;:535,&quot;resizeWidth&quot;:622,&quot;bytes&quot;:16773,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/189117004?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa77aece3-a437-4bd2-bd1a-e5948d723152_535x119.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MsWe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa77aece3-a437-4bd2-bd1a-e5948d723152_535x119.png 424w, https://substackcdn.com/image/fetch/$s_!MsWe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa77aece3-a437-4bd2-bd1a-e5948d723152_535x119.png 848w, https://substackcdn.com/image/fetch/$s_!MsWe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa77aece3-a437-4bd2-bd1a-e5948d723152_535x119.png 1272w, https://substackcdn.com/image/fetch/$s_!MsWe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa77aece3-a437-4bd2-bd1a-e5948d723152_535x119.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Specialized hardware is also worth considering for additional gains on top of software-level kernel bypass. Exanic and Solarflare cards are widely used in the trading industry and provide zero-copy data access, hardware filtering, hardware timestamps, and flexible flow steering.</p><h3>Memory Management</h3><p>Most shared or larger resources get allocated on the heap through dynamic allocation using malloc or new, which requires a system call to expand the heap and introduces additional latency. Low-latency systems avoid this by pre-allocating memory segments at startup and building custom allocators on top of those segments to distribute memory during runtime, with the primary goal of eliminating all system calls from the hot path entirely. At startup you make one large allocation to cover the maximum memory any component might need, and during trading hours your custom allocator distributes from that pool without ever touching the OS. Most large-scale systems package this into a library of containers, allocators, and pools that lets you use vectors, maps, and arrays without the penalty of dynamic allocation by implementing custom versions of these types or custom allocators.</p><p>In practice it is always worth benchmarking different allocators to find the one that performs best for your specific workload, since allocators have different configuration parameters that trade off between latency, throughput, and fragmentation.</p><h3>CPU and NUMA Affinity</h3><p>Modern CPUs use NUMA architecture, dividing processors into nodes where each node has its own processors, caches, and memory, and communication between nodes happens over interconnects that add measurable latency when crossed on a hot path. For critical processes, you want to pin threads to cores within the same NUMA node and ensure that the data those threads access is also allocated on the same node. Non-critical processes like logging, compliance reporting, and observability can run on a separate NUMA node since they care more about throughput than latency.</p><p>An example core allocation for a machine with 8 cores per NUMA node and 2 NUMA nodes might look like this.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8rk_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6687b47-0f02-4f37-a9a3-141091ada387_596x232.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8rk_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6687b47-0f02-4f37-a9a3-141091ada387_596x232.png 424w, https://substackcdn.com/image/fetch/$s_!8rk_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6687b47-0f02-4f37-a9a3-141091ada387_596x232.png 848w, https://substackcdn.com/image/fetch/$s_!8rk_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6687b47-0f02-4f37-a9a3-141091ada387_596x232.png 1272w, https://substackcdn.com/image/fetch/$s_!8rk_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6687b47-0f02-4f37-a9a3-141091ada387_596x232.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8rk_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6687b47-0f02-4f37-a9a3-141091ada387_596x232.png" width="634" height="246.79194630872485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f6687b47-0f02-4f37-a9a3-141091ada387_596x232.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:232,&quot;width&quot;:596,&quot;resizeWidth&quot;:634,&quot;bytes&quot;:22561,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/189117004?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6687b47-0f02-4f37-a9a3-141091ada387_596x232.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8rk_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6687b47-0f02-4f37-a9a3-141091ada387_596x232.png 424w, https://substackcdn.com/image/fetch/$s_!8rk_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6687b47-0f02-4f37-a9a3-141091ada387_596x232.png 848w, https://substackcdn.com/image/fetch/$s_!8rk_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6687b47-0f02-4f37-a9a3-141091ada387_596x232.png 1272w, https://substackcdn.com/image/fetch/$s_!8rk_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6687b47-0f02-4f37-a9a3-141091ada387_596x232.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>This layout lets you run two independent sets of strategies, one per NUMA node, while keeping logging and observability isolated from the critical path.</p><h3><strong>Time Sources and Clocks</strong></h3><p>Getting timestamps right matters a lot more in low-latency systems than in most applications because you are measuring at nanosecond granularity, and the table below covers the main options along with their trade-offs. For cross-host applications, you also need a way to synchronize time across multiple machines at nanosecond precision &#8212; PTP hardware clocks are the standard approach for this.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZmcG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b0b871c-fb7a-4b5a-981b-9f67b63ede0b_658x197.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZmcG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b0b871c-fb7a-4b5a-981b-9f67b63ede0b_658x197.png 424w, https://substackcdn.com/image/fetch/$s_!ZmcG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b0b871c-fb7a-4b5a-981b-9f67b63ede0b_658x197.png 848w, https://substackcdn.com/image/fetch/$s_!ZmcG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b0b871c-fb7a-4b5a-981b-9f67b63ede0b_658x197.png 1272w, https://substackcdn.com/image/fetch/$s_!ZmcG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b0b871c-fb7a-4b5a-981b-9f67b63ede0b_658x197.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZmcG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b0b871c-fb7a-4b5a-981b-9f67b63ede0b_658x197.png" width="658" height="197" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5b0b871c-fb7a-4b5a-981b-9f67b63ede0b_658x197.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:197,&quot;width&quot;:658,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29226,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/189117004?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b0b871c-fb7a-4b5a-981b-9f67b63ede0b_658x197.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZmcG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b0b871c-fb7a-4b5a-981b-9f67b63ede0b_658x197.png 424w, https://substackcdn.com/image/fetch/$s_!ZmcG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b0b871c-fb7a-4b5a-981b-9f67b63ede0b_658x197.png 848w, https://substackcdn.com/image/fetch/$s_!ZmcG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b0b871c-fb7a-4b5a-981b-9f67b63ede0b_658x197.png 1272w, https://substackcdn.com/image/fetch/$s_!ZmcG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b0b871c-fb7a-4b5a-981b-9f67b63ede0b_658x197.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Network Tuning</h3><p>Sockets and hardware both need tuning to get the best latency and throughput out of your setup. The key adjustments are disabling interrupts and relying on polling to avoid breaking application flow, increasing the RX buffer size to reduce packet loss, and increasing the TX buffer backlog queue size, with many other hardware-specific options available depending on your configuration.</p><h3>Observability and Monitoring</h3><p>Monitoring is not optional in low-latency systems because without good observability you cannot see where latency is being spent. The key metrics to track are first-order-out latency for both software and wire-to-wire paths, p50, p95, p99, and p99.9 latencies at both levels, syscall latencies using bcc and bpftrace, CPU utilization to find headroom and spot contention, and NIC-level metrics using tools appropriate to your hardware.</p><h4>Failure Modes</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a5tZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52c5417b-63fb-4c3a-861c-dfd941e8c2a1_948x230.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a5tZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52c5417b-63fb-4c3a-861c-dfd941e8c2a1_948x230.png 424w, https://substackcdn.com/image/fetch/$s_!a5tZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52c5417b-63fb-4c3a-861c-dfd941e8c2a1_948x230.png 848w, https://substackcdn.com/image/fetch/$s_!a5tZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52c5417b-63fb-4c3a-861c-dfd941e8c2a1_948x230.png 1272w, https://substackcdn.com/image/fetch/$s_!a5tZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52c5417b-63fb-4c3a-861c-dfd941e8c2a1_948x230.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!a5tZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52c5417b-63fb-4c3a-861c-dfd941e8c2a1_948x230.png" width="678" height="164.49367088607596" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/52c5417b-63fb-4c3a-861c-dfd941e8c2a1_948x230.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:230,&quot;width&quot;:948,&quot;resizeWidth&quot;:678,&quot;bytes&quot;:47698,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/189117004?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52c5417b-63fb-4c3a-861c-dfd941e8c2a1_948x230.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!a5tZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52c5417b-63fb-4c3a-861c-dfd941e8c2a1_948x230.png 424w, https://substackcdn.com/image/fetch/$s_!a5tZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52c5417b-63fb-4c3a-861c-dfd941e8c2a1_948x230.png 848w, https://substackcdn.com/image/fetch/$s_!a5tZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52c5417b-63fb-4c3a-861c-dfd941e8c2a1_948x230.png 1272w, https://substackcdn.com/image/fetch/$s_!a5tZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52c5417b-63fb-4c3a-861c-dfd941e8c2a1_948x230.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption"></figcaption></figure></div><h2>Deployment Checklist</h2><p>The hardware tuning techniques below are commonly used across trading organizations, but because you are tuning at the hardware level the right set of parameters depends on your specific hardware. These techniques also carry trade-offs like higher power consumption and increased CPU heat, so test carefully and find the optimal configuration for your application before applying them in production.</p><h3>Hardware and BIOS</h3><ul><li><p>Disable C-states and Turbo Boost</p></li><li><p>Set CPU governor to performance mode</p></li><li><p>Lock PCIe ASPM off</p></li><li><p>Enable hugepages</p></li></ul><h3>OS Tuning</h3><ul><li><p>Use isolcpus, nohz_full, and rcu_nocbs kernel parameters</p></li><li><p>Apply sysctl buffer tuning</p></li><li><p>Use mlockall to pin memory and prevent paging</p></li></ul><h3>Process and Code</h3><ul><li><p>Use static preallocation and avoid dynamic containers on the hot path</p></li><li><p>Prefetch and cache-align structs</p></li><li><p>Use lock-free SPSC queues</p></li><li><p>Use TSC for all timing on the critical path</p></li></ul><h2>Common Pitfalls</h2><ul><li><p><strong>False sharing between threads on the same cache line: </strong>This is one of the most common mistakes in multithreaded systems. When two threads write to variables that share a cache line, even if those variables are logically unrelated, the cache coherency protocol treats the entire line as contested and forces unnecessary cross-core coordination. Many cache miss problems in multithreaded environments trace back to this.</p></li><li><p><strong>Chatty IPC over sockets or message queues:</strong> Chatty IPC adds up quickly. Many distributed systems default to message queues, RPCs, or other message-passing mechanisms, but these carry much higher latency in practice even when they scale better horizontally. For low-latency IPC between processes on the same host, shared memory is the right approach.</p></li><li><p><strong>Premature SIMD optimization:</strong> Premature SIMD can actually increase latency when your workload is memory-bound rather than compute-bound. SIMD brings real benefits in the right situations but also adds complexity. Always benchmark before and after adding vectorization because the performance impact can go in either direction depending on your memory access pattern.</p></li><li><p><strong>TSC skew across cores:</strong> TSC skew is a problem that bites HFT systems specifically because they measure at nanosecond granularity. TSC is the standard clock for critical path timing because it does not require a system call, but TSC can drift across cores on the same CPU. Left uncorrected, that skew introduces measurement errors that look like real latency spikes and can obscure genuine performance bugs. Check your CPU documentation for the recommended synchronization approach and apply it regularly.</p></li><li><p><strong>Running without baseline histograms:</strong> Running without baseline histograms makes all of the above invisible. Every parameter including compiler settings, execution environment, hardware, and network can affect latency, and changing a single line of code can produce dramatically different results. Measuring p50 and p99 histograms at every stage of the pipeline is the only reliable way to know where your latency is coming from.</p></li></ul><h3>Further reading /Additional resources</h3><ol><li><p><a href="https://talawah.io/blog/linux-kernel-vs-dpdk-http-performance-showdown/">Linux kernel vs DPDK</a></p></li><li><p>AF_XDP: Zero-Copy Packet Processing in Linux &#8212; Netdev 2024:  </p></li></ol><div id="youtube2-cSdQIISFx08" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;cSdQIISFx08&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/cSdQIISFx08?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><ol start="3"><li><p><a href="https://dev.to/frosnerd/libmalloc-jemalloc-tcmalloc-mimalloc-exploring-different-memory-allocators-4lp3">Benchmarking allocators</a></p></li><li><p><a href="https://nordvarg.com/blog/time-synchronization-distributed-systems">Time Synchronization in Distributed Trading Systems</a></p></li><li><p><em><a href="https://api.pageplace.de/preview/DT0400.9780136624547_A41216512/preview-9780136624547_A41216512.pdf">BPF Performance Tools</a> </em>by Brendan Gregg</p></li><li><p><a href="https://www.intel.com/content/www/us/en/developer/articles/technical/optimizing-computer-applications-for-latency-part-1-configuring-the-hardware.html">Intel BIOS optimizations</a> </p></li><li><p><a href="https://access.redhat.com/sites/default/files/attachments/201501-perf-brief-low-latency-tuning-rhel7-v2.1.pdf">RHEL 7 low-latency tuning guide </a></p></li><li><p>IPC vs shared memory:</p></li></ol><div id="youtube2-LDHnBW2v_D4" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;LDHnBW2v_D4&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/LDHnBW2v_D4?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><ol start="9"><li><p>Incorrect use of SIMD:</p></li></ol><div id="youtube2-GleC3SZ8gjU" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;GleC3SZ8gjU&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/GleC3SZ8gjU?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Coroutines]]></title><description><![CDATA[The complete Chapter 15: Coroutines from Kotlin for Java Developers by Jos&#233; Dimas Luj&#225;n Castillo and Ron Veen (Packt, 2025)]]></description><link>https://deepengineering.substack.com/p/coroutines</link><guid isPermaLink="false">https://deepengineering.substack.com/p/coroutines</guid><dc:creator><![CDATA[Ron veen]]></dc:creator><pubDate>Thu, 12 Feb 2026 09:04:47 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e24774ab-3f3e-4526-86a4-4de740c0206a_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this chapter, we will explore one of Kotlin&#8217;s most powerful and transformative features &#8211; <strong>coroutines</strong>. Designed to simplify and enhance asynchronous programming, coroutines allow developers to write non-blocking, concurrent code with ease and clarity. By offering a structured and intuitive approach to handling tasks such as API calls, database operations, and UI updates, coroutines have become a cornerstone for modern Kotlin development.</p><p>Unlike traditional approaches to asynchronous programming, such as threads and callbacks, coroutines eliminate complexity by providing a way to write asynchronous code that looks and behaves like synchronous code. This chapter introduces you to the fundamentals of coroutines, teaching you how to avoid issues with callbacks and enabling you to manage concurrency gracefully and efficiently.</p><p>Kotlin&#8217;s coroutines framework is built on top of lightweight threads, providing developers with the tools to execute tasks in parallel, handle timeouts and cancellations, and switch between different execution contexts. Throughout this chapter, we will uncover how coroutines work, why they are an improvement over traditional concurrency models, and how to leverage them to build robust and responsive applications.</p><p>We will start by understanding what coroutines are and how they differ from threads and other concurrency mechanisms. Next, we will dive into the implementation of coroutines, learning about coroutine builders such as <code>launch</code> and <code>async</code>, as well as the importance of suspending functions in asynchronous workflows. We will then explore strategies for managing timeouts and cancellations, ensuring our applications remain efficient and responsive under various conditions. Finally, we will tackle the advanced concepts of context and scope, which are essential for organizing and managing coroutine life cycles effectively.</p><p>By the end of this chapter, you will have a comprehensive understanding of Kotlin coroutines, enabling you to write clean, maintainable, and high-performing asynchronous code. Whether you&#8217;re developing Android applications, server-side services, or any system requiring concurrent operations, mastering coroutines will empower you to tackle complex challenges with confidence and precision.</p><p>We&#8217;re going to cover the following topics:</p><ul><li><p>What is a coroutine?</p></li><li><p>Coroutine implementation</p></li><li><p>Timeouts and cancellations</p></li><li><p>Contexts and scope</p></li></ul><div><hr></div><h1>Technical requirements</h1><p>You can use any text editor or <strong>Integrated Development Environment</strong> (<strong>IDE</strong>) of your choice with the Java language. We recommend IntelliJ IDEA <strong>Community Edition</strong> (<strong>CE</strong>). This tool already comes with everything you need to work with both Java and Kotlin. The software requirements for this chapter, with required versions, are as follows:</p><ul><li><p>Java JDK 17 or higher</p></li><li><p>IntelliJ IDEA CE</p></li><li><p>Kotlin 1.x or 2.x</p></li></ul><p>The code used in the chapters can be found in the book&#8217;s accompanying GitHub repository: <a href="https://github.com/PacktPublishing/Kotlin-for-Java-Developers">https://github.com/PacktPublishing/Kotlin-for-Java-Developers</a>.</p><div><hr></div><h1>What is a coroutine?</h1><p>To simplify what a coroutine is, let&#8217;s first look at the official definition: &#8220;<em>A coroutine is an instance of a suspendable computation.</em>&#8221; While technically accurate, it may seem too abstract. Instead, let&#8217;s break it down with a practical and relatable analogy.</p><p>Imagine you are dining at a restaurant. A waiter comes to your table, takes your food order, and heads to the kitchen to pass the order on to the chefs. Now, imagine the waiter stays in the kitchen, doing nothing but waiting for your food to be prepared. This would be inefficient, as the waiter could be serving drinks, attending to other tables, or bringing bread to your table while the food is being cooked.</p><p>A smarter approach is for the waiter to leave the instructions with the kitchen staff and return to attend to other customers or tasks. Once your food is ready, the kitchen would alert the waiter, who would promptly bring the dishes to your table.</p><p>This efficiency is exactly how <strong>coroutines</strong> operate. In programming, there&#8217;s a &#8220;main thread&#8221; (like the waiter), which handles primary tasks (like serving the diners in our analogy). However, certain tasks (such as cooking the food) can take an unknown amount of time. These tasks are better handled asynchronously, without blocking the main thread.</p><h2>The restaurant analogy</h2><p>To make the concept of coroutines easier to grasp, let&#8217;s stick with the restaurant analogy. This comparison helps illustrate how asynchronous tasks and concurrency work in Kotlin using coroutines:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Hqpe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5668a984-9948-4c92-ae31-49ada2017605_572x325.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Hqpe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5668a984-9948-4c92-ae31-49ada2017605_572x325.png 424w, https://substackcdn.com/image/fetch/$s_!Hqpe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5668a984-9948-4c92-ae31-49ada2017605_572x325.png 848w, https://substackcdn.com/image/fetch/$s_!Hqpe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5668a984-9948-4c92-ae31-49ada2017605_572x325.png 1272w, https://substackcdn.com/image/fetch/$s_!Hqpe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5668a984-9948-4c92-ae31-49ada2017605_572x325.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Hqpe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5668a984-9948-4c92-ae31-49ada2017605_572x325.png" width="572" height="325" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5668a984-9948-4c92-ae31-49ada2017605_572x325.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:325,&quot;width&quot;:572,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 11.1 &#8211; Restaurant analogy for coroutines&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 11.1 &#8211; Restaurant analogy for coroutines" title="Figure 11.1 &#8211; Restaurant analogy for coroutines" srcset="https://substackcdn.com/image/fetch/$s_!Hqpe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5668a984-9948-4c92-ae31-49ada2017605_572x325.png 424w, https://substackcdn.com/image/fetch/$s_!Hqpe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5668a984-9948-4c92-ae31-49ada2017605_572x325.png 848w, https://substackcdn.com/image/fetch/$s_!Hqpe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5668a984-9948-4c92-ae31-49ada2017605_572x325.png 1272w, https://substackcdn.com/image/fetch/$s_!Hqpe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5668a984-9948-4c92-ae31-49ada2017605_572x325.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 11.1 &#8211; Restaurant analogy for coroutines</figcaption></figure></div><p>Let&#8217;s try to understand this analogy:</p><ol><li><p><strong>Main thread as the waiter</strong>: The main thread is responsible for visible and interactive tasks (like serving diners). It shouldn&#8217;t be blocked (or stuck in the kitchen).</p></li><li><p><strong>Asynchronous tasks as the kitchen</strong>: Some operations, like fetching data from the internet or reading from a database, are similar to cooking food in the kitchen. These tasks can take time and don&#8217;t need the waiter (the main thread) to stand idle while waiting for them to finish.</p></li><li><p><strong>Coroutines as the waiter&#8217;s delegation</strong>: The waiter can delegate tasks such as food preparation to the kitchen while continuing to handle other responsibilities (serving drinks, taking new orders). Similarly, coroutines allow you to &#8220;pause&#8221; tasks and resume them later without stopping the main thread.</p></li><li><p><strong>Concurrent coroutines for multiple tasks</strong>: Just as a waiter can manage multiple orders at once, you can create multiple coroutines to handle different tasks simultaneously. For instance, one chef can prepare appetizers, another can handle main courses, and yet another can prepare desserts &#8211; all while the waiter continues serving diners.</p></li></ol><h2>In programming terms</h2><p>Kotlin simplifies asynchronous programming, but it doesn&#8217;t include heavy low-level APIs in its standard library. Instead, it provides just enough tools for libraries, such as <code>kotlinx.coroutines</code>, to build powerful coroutine-based utilities. Unlike some other languages, Kotlin doesn&#8217;t treat <code>async</code> and <code>await</code> as keywords or include them directly in the language. Instead, it uses suspending functions, which make asynchronous code safer and easier to use compared to traditional approaches such as futures or promises.</p><p>To better understand how coroutines work, let&#8217;s map their behavior to real-world and programming concepts. This helps clarify how Kotlin approaches asynchronous programming in a more intuitive way:</p><ul><li><p><strong>Main thread</strong>: The core execution thread that interacts with users (like serving diners).</p></li><li><p><strong>Coroutine</strong>: A lightweight task that runs asynchronously and can be paused and resumed without blocking the main thread.</p></li><li><p><strong>Suspension</strong>: Like the waiter leaving instructions in the kitchen, a coroutine can &#8220;pause&#8221; its execution, allowing the main thread to continue working until the task is complete.</p></li></ul><p>The <code>kotlinx.coroutines</code> library, developed by JetBrains, is a feature-rich library that provides high-level tools for working with coroutines. It includes useful functions such as <code>launch</code> and <code>async</code>, which simplify working with background tasks.</p><h2>Explanation of coroutines for a Java programmer</h2><p>It&#8217;s important to know that Java does not have coroutines. However, there are many ways we can achieve the results that coroutines achieve, and it is very likely that those of you who know Java might know some of these ways.</p><p>While there are projects such as Project Loom and Quasar that attempt to bring coroutine-like behavior to Java, they require third-party dependencies and are not yet widely adopted in the Java ecosystem.</p><p>Java thread handling was enhanced with virtual threads in version 21. They are a more lightweight threading model. Yet, virtual threads and coroutines serve different purposes. Virtual threads are primarily used for high-throughput, I/O-bound server applications based on a thread-per-request model. Coroutines, on the other hand, are typically used for various asynchronous tasks.</p><p>Note that it is perfectly possible for a coroutine to use a virtual thread to execute its tasks, especially if these tasks include blocking I/O operations.</p><p>In this section, we explored the fundamental concept of coroutines, learning how they provide a powerful and efficient alternative to traditional asynchronous programming models such as threads, futures, and promises. Using the analogy of a waiter in a restaurant, we visualized how coroutines delegate long-running tasks to the background while keeping the main thread free to handle interactive or essential tasks. Key characteristics of coroutines, such as their non-blocking nature, ease of readability, and lightweight execution, were highlighted as significant advantages over Java&#8217;s concurrency options. For Java developers, the comparison to traditional tools such as threads and futures underlined the simplicity and efficiency that coroutines bring to the table.</p><p>In the next section, we will dive deeper into the practical aspects of working with coroutines. You&#8217;ll learn how to create and manage coroutines using coroutine builders and suspending functions, setting the foundation for writing clean and effective asynchronous code in Kotlin. Let&#8217;s begin our hands-on journey into coroutine implementation!</p><div><hr></div><h1>Coroutine implementation</h1><p>Now let&#8217;s see how to implement coroutines in a Kotlin project. For this, we&#8217;ll start from scratch. First, we&#8217;ll do something very simple; we can consider it the &#8220;<code>Hello World</code>&#8220; of coroutines. Then we&#8217;ll increase the difficulty of the example and show other features.</p><p>We will start by adding the possibility of using coroutines in our project, and for that, we will add the dependency.</p><p>One thing to note is that using <code>kotlinx.coroutines</code> has become a convention in the Kotlin community, making it easier to collaborate and understand code.</p><p>We don&#8217;t recommend trying to memorize the versions, since over time, these change, and they change faster than you can imagine. It is more important to know what the official <code>kotlinx.coroutines</code> repository is. GitHub is a reliable source to obtain information about the latest versions and news: <a href="https://github.com/Kotlin/kotlinx.coroutines">https://github.com/Kotlin/kotlinx.coroutines</a>.</p><p>In general terms, we can say that we need to add the coroutines dependency. We can find it in our <code>build.gradle.kts</code> file, and the configuration would be something like this:</p><pre><code><code>dependencies {
testImplementation(kotlin("test"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
}</code></code></pre><p>For coroutines, we add the one reference to <code>kotlinx 1.8.0</code>, which is enough for what we will do in this chapter. Now, in our first contact with coroutines, we are going to write <code>Hello World</code>. This will be our starting point for this entire chapter. This is the code:</p><pre><code><code>import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
    launch {
        delay(1000L)
        println("Kotlin World! ")
    }
    println("Hello")
}</code></code></pre><p>The imports come from the <code>kotlinx.coroutines</code> package, which contains the main tools for working with coroutines in Kotlin:</p><ul><li><p><code>delay</code>: A function that suspends the execution of a coroutine for a specific time (in milliseconds) without blocking the thread.</p></li><li><p><code>launch</code>: A function that starts a new coroutine in parallel with the existing execution.</p></li><li><p><code>runBlocking</code>: A function that runs a blocking coroutine, useful for simple examples or tests where we want to block the main thread until the inner coroutines finish.</p></li></ul><p>The imports should be familiar. The first part that is noticeably different from basic &#8220;<code>Hello World</code>&#8220; code is the following:</p><pre><code><code>fun main() = runBlocking {</code></code></pre><p>The <code>runBlocking</code> instruction is used here to allow <code>main</code> to run as a coroutine. It blocks the main thread until all coroutines within its block finish executing.</p><p>This ensures that the program does not terminate before the coroutines finish.</p><p>Let&#8217;s look at the <code>launch</code> instruction:</p><pre><code><code>launch {
    delay(1000L)
    println("Kotlin World!")
}</code></code></pre><p><code>launch</code> creates a new coroutine. This coroutine runs concurrently with the rest of the code inside <code>runBlocking</code>.</p><p>Inside this coroutine, we have <code>delay(1000L)</code>, which suspends the coroutine for 1 second (1,000 milliseconds) without blocking the thread. This means that the main thread is free to perform other tasks in the meantime, and after one second, it prints <code>"Kotlin World!"</code>.</p><p>Finally, we have the last instruction:</p><pre><code><code>println("Hello")</code></code></pre><p>Since the coroutine does not block the thread, this message is printed before the coroutine finishes its task.</p><p>Summarizing the flow, we can describe it as follows:</p><ol><li><p>The main function is executed and enters the <code>runBlocking</code> block.</p></li><li><p>Inside <code>runBlocking</code>, the following occurs:</p><ol><li><p>A coroutine is launched with <code>launch.</code></p></li><li><p>Execution continues without waiting for the coroutine to finish, so <code>println("Hello")</code> is executed immediately.</p></li></ol></li><li><p>In the meantime, the coroutine executes<code> delay(1000L)</code>, which suspends the coroutine for one second.</p></li><li><p>After the delay, it prints <code>"Kotlin World!"</code>.</p></li><li><p>The program waits inside <code>runBlocking</code> until all coroutines inside its block finish. Only then does <code>runBlocking</code> complete and the program terminates.</p></li></ol><p>After executing the code, we see the following as a result:</p><pre><code><code>Hello
Kotlin World!</code></code></pre><p>The reason we started with <code>Hello World</code> is simple &#8211; we are studying the simplest possible case to show how coroutines allow concurrent tasks (such as waiting a second) to be expressed without blocking the entire scope of execution. In this example, <code>runBlocking</code> blocks the main thread until all coroutines inside complete, but the coroutine launched with <code>launch</code> runs concurrently within that scope.</p><p>Compared to a traditional threading approach, this is more efficient and easier to read.</p><p>We introduced the concepts of <code>delay</code> and <code>launch</code> in a simple context. Now we will look at another example. We will simulate a restaurant scenario to demonstrate how coroutines handle asynchronous tasks.</p><p>We want to demonstrate how to perform multiple tasks simultaneously</p><p>We are going to show how not to block the main thread while waiting for long-running tasks. We will illustrate coordination between different asynchronous operations, something similar to what we mentioned about the waiter and the restaurant. This is the new code:</p><pre><code><code>import kotlinx.coroutines.*
fun main() = runBlocking {
    println("Taking order...")
    // Coroutine 1: Preparing food in the kitchen
    val food = async { prepareFood() }
    // Coroutine 2: Serving drinks
    serveDrinks()
    // Wait for food to be ready and serve it
    println("Food is ready: ${food.await()}")
}
suspend fun prepareFood(): String {
    delay(3000) // Simulate food preparation
    return "Delicious meal"
}
fun serveDrinks() {
    println("Serving drinks to the table...")
}</code></code></pre><p>The <code>runBlocking</code> block represents the main thread where primary actions (such as interacting with diners) occur. As mentioned before, the <code>runBlocking</code> instruction is a function that runs a block of code as a blocking coroutine. This means that the program will not end until all the code inside this block finishes. In this example, it represents the main thread, which acts as the waiter handling multiple tasks.</p><pre><code><code>println("Taking order...")</code></code></pre><p>The preceding line of code is printed immediately on the main thread (representing the waiter).</p><pre><code><code>val food = async { prepareFood() }</code></code></pre><p><code>async</code> launches a new coroutine to execute the <code>prepareFood()</code> function concurrently.</p><p><code>async</code> is a function that returns a <code>Deferred</code> object, which is like a &#8220;future&#8221; in Java. This object can be awaited or revisited later to get the results of the operation.</p><p>In this case, it represents the cook who starts preparing the food in the background.</p><p>Let us look at the <code>prepareFood</code> function:</p><pre><code><code>suspend fun prepareFood(): String {
    delay(3000) // Simulate food preparation
    return "Delicious meal"
}</code></code></pre><p>The <code>prepareFood</code> function is a suspended function, meaning that it can pause its execution and later resume exactly where it left off, without blocking the underlying thread while it is waiting. However, a <code>suspend</code> function can only be called from another <code>suspend</code> function or within a coroutine. It is also important to note that while the thread is not blocked, the coroutine that invokes <code>prepareFood</code> will remain suspended until the function resumes.</p><p><code>delay(3000)</code> simulates a meal preparation time of three seconds. During this time, the coroutine is suspended, allowing the main thread to do other tasks.</p><p>After the delay, it returns <code>Delicious meal</code>.</p><p>While the food is being prepared (the async coroutine is suspended), the main thread continues to execute this function:</p><pre><code><code>serveDrinks()</code></code></pre><p>It prints the message <code>Serving drinks to the table...</code>.</p><p>Here, the waiter (main thread) serves drinks while waiting for the cook to finish the food:</p><pre><code><code>println("Food is ready: ${food.await()}")</code></code></pre><p><code>food.await()</code> pauses execution at this point until the <code>prepareFood()</code> coroutine finishes and returns its result.</p><p>Once <code>prepareFood()</code> returns <code>Delicious meal</code>, the main thread prints the following:</p><pre><code><code>Food is ready: Delicious meal</code></code></pre><p>This is how the flow goes:</p><ul><li><p><code>runBlocking</code> starts the main block.</p></li><li><p>The waiter takes the order and starts serving the drinks.</p></li><li><p>Meanwhile, the cook (coroutine) prepares the food in the background.</p></li><li><p>After three seconds, the food is ready and the waiter serves it.</p></li></ul><p>Returning to our analogy, we&#8217;ve already seen the following:</p><ul><li><p><code>Waiter</code>: This is the main thread that organizes tasks. It can serve drinks and handle other orders without getting stuck waiting for the food to be ready.</p></li><li><p><code>Cook</code>: This is a coroutine that works independently in the background, preparing the food.</p></li><li><p><code>async</code> and<code> await</code>: <code>async</code> starts the cook&#8217;s work and <code>await</code> makes sure the waiter waits for the result before serving the food.</p></li><li><p><code>delay</code>: This simulates the time it takes the cook to prepare the food.</p></li></ul><p>This is what we see after running the program:</p><pre><code><code>Taking order...
Serving drinks to the table...
Food is ready: Delicious meal</code></code></pre><p>The main goal of this exercise was to demonstrate how coroutines allow you to do several things &#8220;at the same time&#8221; efficiently, just like a good waiter in a restaurant.</p><p>We are going to show another example that simulates loading book data from an API using coroutines in Kotlin. The repository (<code>BookRepository</code>) has a suspended function that simulates a two-second delay before returning the book data. In the <code>BookScreen</code> class, <code>withContext(Dispatchers.IO)</code> is used to execute the task in a context suitable for I/O operations, displaying success or error messages depending on the result. This demonstrates how to handle asynchronous tasks in an efficient and readable way. But we will build it in parts.</p><pre><code><code>import kotlinx.coroutines.*
// Simulate a repository that fetches book data
class BookRepository {
    // Simulate an API call to fetch a book
    suspend fun fetchBookFromApi(bookId: String): Book {
        delay(2000) // Simulate network response time
        return Book(bookId, "Clean Code", "Robert C. Martin")
    }
}</code></code></pre><p><code>BookRepository</code> has a <code>suspend</code> function, <code>fetchBookFromApi</code>, which simulates calling an API to get the data for a book.</p><p>It uses <code>delay(2000)</code> to represent a two-second network delay, and then returns a <code>Book</code> object as the result.</p><p>We add this code, which defines the class that the <code>fetchBookFromApi</code> function returns:</p><pre><code><code>data class Book(
    val id: String,
    val title: String,
    val author: String
)</code></code></pre><p><code>Book</code> is a data class that represents information about a book, with properties such as <code>id</code>, <code>title</code>, and <code>author</code>.</p><pre><code><code>class BookScreen {
    private val repository = BookRepository()
    fun loadBookData() = runBlocking {
    try {
        println(" Loading book information...")
        // Send the request in a coroutine
        val book = withContext(Dispatchers.IO) {
            repository.fetchBookFromApi("9780132350884")
}</code></code></pre><p><code>BookScreen</code> defines the <code>loadBookData</code> method, which uses <code>runBlocking</code> to handle loading book data within a coroutine.</p><p>The <code>loadBookData</code> method uses <code>withContext(Dispatchers.IO)</code> to perform the book-fetching task in a context optimized for I/O operations.</p><p>Finally, we define <code>main</code>, which will allow us to execute the code:</p><pre><code><code>fun main() {
    val screen = BookScreen()
    screen.loadBookData()
}</code></code></pre><p>In the <code>main</code> function, you simply create an instance of <code>BookScreen</code> and call the <code>loadBookData</code> method to start the process.</p><p>Summarizing the program flow, this would be as follows:</p><ul><li><p>It prints &#8220;<code>Loading book information...</code>&#8220;</p></li><li><p>It waits two seconds, simulating the API response.</p></li><li><p>If everything goes well, it prints <code>"Book loaded: Clean Code by Robert C. Martin"</code>.</p></li><li><p>If an error occurs, it prints <code>"Error loading book information: ..."</code></p></li></ul><p>Let&#8217;s take a closer look at what&#8217;s happening in this exercise. We&#8217;ve successfully used <code>runBlocking</code> and <code>withContext</code> to run the task asynchronously and on a separate thread (<code>Dispatchers.IO</code>), which is ideal for I/O tasks such as API calls.</p><p>This approach is ideal for I/O tasks such as API calls because it allows us to perform these operations without blocking the main thread.</p><p>By offloading the work to a different dispatcher, we ensure that time-consuming operations (such as reading from a database, writing to a file, or calling an external API) don&#8217;t freeze the UI or other critical processes. In Kotlin coroutines, <code>Dispatchers.IO</code> is specifically optimized for these types of tasks, giving us efficient and responsive code even when dealing with slow or unpredictable external systems.</p><p>We have included a <code>try-catch</code> block to catch possible exceptions that may occur during the API call, which is a good practice to make your code more robust.</p><p>Simulating API response time with a delay is useful to understand the behavior of coroutines in real-world scenarios.</p><p>In this section, we introduced the foundational ideas behind coroutines in Kotlin, focusing on how they enable writing asynchronous, non-blocking code that remains readable and efficient. We illustrated real-world analogies and scenarios to highlight how coroutines support structured concurrency and safe context switching, making them a powerful tool for managing concurrent tasks in modern applications.</p><p>It&#8217;s important to clarify when to use these different coroutine builders:</p><ul><li><p><code>runBlocking</code> is typically used to bridge blocking and non-blocking code, for example, in tests or the <code>main</code> function of a small program. It blocks the current thread until its coroutine body finishes executing.</p></li><li><p>In contrast, using coroutine builders such as <code>launch</code> or <code>async</code> within an existing coroutine scope, combined with context switching (<code>withContext</code>), lets you run asynchronous code without blocking any thread, ensuring better performance and responsiveness.</p></li></ul><p>It is important to note, however, that coroutines are not magical. If you call blocking operations inside a <code>suspend</code> function &#8211; for example, using <code>Thread.sleep()</code> or synchronous I/O calls such as JDBC &#8211; you will still block the underlying thread. To fully benefit from coroutines, blocking calls should be replaced with non-blocking equivalents whenever possible.</p><p>So, while <code>runBlocking</code> is useful in specific entry points, using structured concurrency with <code>launch</code>, <code>async</code>, and <code>withContext</code> is preferred in production code to handle tasks concurrently and safely without halting your program&#8217;s execution.</p><p>Now that we have a solid understanding of how to implement and use coroutines effectively, the next section will introduce timeouts and cancellations. You&#8217;ll learn how to gracefully handle scenarios where tasks take too long or need to be interrupted, ensuring your applications remain responsive and efficient under various conditions. Let&#8217;s continue exploring how Kotlin coroutines can simplify even the most complex asynchronous workflows!</p><div><hr></div><h1>Timeouts and cancellations</h1><p>Now we will see two important elements that are good to know about in coroutines: timeouts and cancellations. Timeouts are time limits that you set for an operation. If the operation takes longer than the specified time, it is automatically cancelled.</p><p>A simple way to describe them using examples of daily activities would be as follows:</p><ul><li><p>Ordering a pizza and getting the notice: &#8220;If it doesn&#8217;t arrive in 30 minutes, we cancel the order and refund you&#8221;.</p></li><li><p>When an ATM cancels the operation if we don&#8217;t respond within a certain time.</p></li></ul><p>Cancellations, as their name suggests, allow us to stop a coroutine in the middle of its execution. Let&#8217;s look at a couple of examples:</p><ul><li><p>Canceling a download in progress.</p></li><li><p>Canceling a bank transfer before it is completed.</p></li></ul><p>Thinking of timeouts and cancellations as elements that we can use, we could easily give some situations where we could use them:</p><ul><li><p>API calls that should not take too long.</p></li><li><p>File downloads that can be cancelled.</p></li><li><p>Database operations with a time limit.</p></li><li><p>Background processes that can be cancelled by the user.</p></li></ul><p>Taking our previous example of books as a basis, we are now going to modify it to have both cancellation and timeouts.</p><p>The definitions of <code>BookRepository</code> and <code>Book</code> remain the same; we just add more time to <code>Delay</code>. Before, we had <code>2000</code>, but we will change the value to <code>3000</code>. We increased the time period to later; we set a time limit of two seconds and allowed three seconds to execute it (the timeout):</p><pre><code><code>class BookRepository {
    // Simulates an API call to get a book
    suspend fun fetchBookFromApi(bookId: String): Book {
        delay(3000) // Simulates network response time
        return Book(bookId, "Clean Code", "Robert C. Martin")
    }
}
data class Book(
    val id: String,
    val title: String,
    val author: String
)</code></code></pre><p>Now we will change <code>launch</code> to make it look like this:</p><pre><code><code>val job = launch {
    try {
        println(" Loading book information...")
        // Set a timeout for the operation
        val book = withTimeout(2000) { // 2 second timeout
            repository.fetchBookFromApi("9780132350884")
        }
        println(" Book loaded: ${book.title} by ${book.author}")
    } catch (e: TimeoutCancellationException) {
        println(" Timeout exceeded when loading book.")
    } catch (e: Exception) {
        println(" Error loading book information: ${e.message}")
    }
}</code></code></pre><p>We set a time limit of 2,000 milliseconds to complete the <code>fetchBookFromApi</code> operation. If the task does not finish within this time, <code>TimeoutCancellationException</code> is thrown. This mechanism is essential in applications where it is critical to avoid long blockages and ensure fast responses.</p><p>In this statement, we can see that the approach uses <code>job</code>, as declared with <code>launch</code>, to handle the execution of the coroutine:</p><pre><code><code>val job = launch {</code></code></pre><p>This allows the job to be explicitly cancelled if necessary, providing flexibility to stop execution in specific situations. For example, the job could be cancelled with a call to <code>job.cancelAndJoin()</code> if the task is detected to be no longer relevant or if an external event occurs.</p><p>Exception handling is a key part of this approach:</p><pre><code><code>} catch (e: TimeoutCancellationException) {
println(" Timeout exceeded when loading book.")</code></code></pre><p>These last lines demonstrate how to handle scenarios where the time limit is exceeded, ensuring that the system can respond appropriately without crashing. Other exceptions are also handled to catch generic errors during execution.</p><p>While this approach is powerful, it adds complexity to the code. It requires explicitly handling both the job and exceptions, which may be unnecessary for simple tasks. However, in cases where strict time limits or precise cancellations are needed, this structure proves invaluable.</p><p>Outside of <code>loadBookData</code>, we will also modify the code in the following way:</p><pre><code><code>// Change: Cancel the job after 4 seconds
delay(4000)
println(" Canceling the loading of the book...")
job.cancelAndJoin() // Cancel the job and wait for it to finish
println("Task cancelled.")</code></code></pre><p>Notice that we have introduced a delay of 4,000 milliseconds and manual cancellation. Also notice that we use a job to cancel the task explicitly with <code>job.cancelAndJoin()</code>.</p><p>The cancellation and timeout scenario introduces a more controlled approach to handling asynchronous tasks in Kotlin. Using <code>launch</code> and <code>withTimeout</code>, a time limit is set for the execution of an operation.</p><p>This ensures that the task does not exceed the time we decide, throwing an exception (<code>TimeoutCancellationException</code>) if the time runs out. Also, by encapsulating the logic in a job, the task can be cancelled explicitly when necessary, optimizing resources and allowing better management of multiple concurrent tasks.</p><p>This example we developed includes robust exception handling to differentiate between a timeout and other errors. In summary, this model is ideal for applications where time limits and cancellation of irrelevant or delayed tasks are required, although it adds a bit more complexity to the code.</p><p>This section emphasized the importance of timeouts and cancellations in effective coroutine management. By enforcing time limits and enabling controlled interruption of tasks, Kotlin coroutines provide mechanisms to keep applications responsive and resilient, especially when dealing with potentially long-running operations.</p><p>We also introduced the concept of cancellations, allowing tasks to be stopped explicitly when they are no longer needed. By leveraging the j<code>ob</code> object, we illustrated how to manage and cancel ongoing tasks using <code>job.cancelAndJoin()</code>, ensuring efficient resource management. Additionally, we incorporated robust exception handling to distinguish between timeout-specific issues (<code>TimeoutCancellationException</code>) and other errors, adding resilience to our code.</p><p>By combining these techniques, we showed how to handle asynchronous tasks in a controlled and efficient manner, enabling developers to create responsive applications that gracefully handle delays and interruptions. This approach is particularly useful in scenarios involving API calls, file downloads, or user-cancelled operations, providing flexibility and reliability.</p><p>Next, we&#8217;ll explore contexts and scopes, diving deeper into how coroutines manage their execution environments and life cycle. You&#8217;ll learn about coroutine dispatchers, structured concurrency, and how to properly manage coroutine life cycles to maintain clean, predictable, and efficient code. Let&#8217;s continue building our understanding of Kotlin coroutines with these advanced concepts!</p><div><hr></div><h1>Contexts and scope</h1><p>Context and scope are fundamental concepts in the world of Kotlin coroutines and play a crucial role in managing their life cycle and behavior.</p><p>A context in Kotlin coroutines is like a container that groups key information about how and where a coroutine will run. This context includes the following:</p><ul><li><p><strong>Dispatcher</strong>: Defines which thread or thread group the coroutine will run on, as follows:</p><ul><li><p><code>Dispatchers.IO</code> for I/O operations (such as working with files or network calls).</p></li><li><p><code>Dispatchers.Main</code> for updating the UI.</p></li></ul></li><li><p><strong>Job</strong>: Represents the specific task the coroutine performs. It also allows you to cancel it or monitor its progress.</p></li><li><p><strong>Other attributes</strong>: Can include things such as the following:</p><ul><li><p><code>CoroutineName</code>: A useful identifier to distinguish coroutines.</p></li><li><p><code>NonCancellable</code>: Indicates that the coroutine should not be cancelled, even if its parent or associated scope is.</p></li></ul></li></ul><p>In addition, you can put custom elements into the coroutine context, such as an <strong>Mapped Diagnostic Context</strong> (<strong>MDC</strong>) for logging. It is also important to remember that thread-local variables are not effective with coroutines, since a coroutine may resume on a different thread. If you need thread-local-like behavior, you should use <code>ThreadContextElement</code> to propagate context across suspensions.</p><p>In short, a coroutine&#8217;s context sets up its execution environment: it defines where it runs, how it interacts with other processes, and what rules it should follow while working. It&#8217;s like giving the coroutine a roadmap and tools to get it to perform its task correctly.</p><p>The code for <code>BookRepository</code> and <code>Book</code> remains the same; we don&#8217;t have to make any changes.</p><p>The changes start with the <code>BookScreen</code> code. We add the scope as follows:</p><pre><code><code>class BookScreen {
    private val repository = BookRepository()
    private val scope = CoroutineScope(Dispatchers.Main + Job())</code></code></pre><p><code>CoroutineScope</code> is used, which automatically encapsulates the context and the associated job. With this change, instead of the previously temporary scope used with <code>runBlocking</code>, now there is a permanent scope with <code>CoroutineScope(Dispatchers.Main + Job())</code>.</p><p>This makes it easier to manage related tasks, allowing all coroutines associated with the scope to be cancelled with a single call (<code>scope.cancel()</code>).</p><p>We need better organization for larger applications or those with multiple coroutines.</p><p>Let&#8217;s make some changes in the structure:</p><pre><code><code>fun loadBookDetails(bookId: String) {
    scope.launch {
    withContext(Dispatchers.IO) {
        // code here
        }
    }
}</code></code></pre><p>We notice with these changes that a specific scope is defined at the class level and dispatchers are explicitly specified. In addition, we are now handling specific execution contexts, and we are going to allow global cancellation of all coroutines.</p><p>Regarding the execution context, we can also say that with the previous code, dispatchers were not specified, and now we are indicating one, <code>dispatchers.IO</code>, for input and output operations.</p><p>We also need to change <code>val book</code>. It should look like this:</p><pre><code><code>val book = withTimeout(3000) {
    // Set a timeout of 3 seconds
    withContext(Dispatchers.IO) {
        // Run the task in the I/O Dispatcher
        repository.fetchBookFromApi(bookId)
    }
}</code></code></pre><p>We add this <code>function(cancelAllTasks)</code> after <code>loadBookDetails</code>:</p><pre><code><code>fun cancelAllTasks() {
    scope.cancel() // Cancels all coroutines in the Scope
    println("All tasks were cancelled.")
}</code></code></pre><p>We centralize canceling all active coroutines of the scope with <code>scope.cancel()</code> in the <code>cancelAllTasks()</code> method.</p><p>The main code part now looks like this:</p><pre><code><code>fun main() {</code><strong>
</strong><code>    val screen = BookScreen()</code><strong>
</strong><code>    // Start loading book details</code><strong>
</strong><code>    screen.loadBookDetails("1")</code><strong>
</strong><code>    // Simulate task cancellation after 5 seconds</code><strong>
</strong><code>    runBlocking {</code><strong>
</strong><code>        delay(5000)</code><strong>
</strong><code>        screen.cancelAllTasks()</code><strong>
</strong><code>    }</code><strong>
</strong><code>}</code></code></pre><p>With the <code>loadBookDetails</code> instruction, we will start loading the book details to display them. With the second part, <code>runBlocking</code>, we would be simulating the cancellation of tasks after five seconds.</p><p>So, let&#8217;s get to the differences:</p><ul><li><p><strong>Life cycle management</strong>:</p><ul><li><p><code>runBlocking</code> blocks the main thread until all the internal coroutines complete. It&#8217;s useful for testing or small examples, but it&#8217;s not ideal for real applications because it freezes the main thread while it waits. The problem is that the thread is blocked, which affects performance if there are more concurrent tasks.</p></li><li><p>Using <code>CoroutineScope</code> acts as a container that allows you to manage all the coroutines created within it. This is ideal for real-world applications because it allows you to manage the life cycle of multiple coroutines in a centralized manner.</p></li><li><p>We can see that in the code, we can now cancel all related coroutines at once using <code>scope.cancel()</code>. This is useful in situations where, for example, a user leaves a screen in a mobile app.</p></li></ul></li><li><p><strong>Dispatchers</strong>:</p><ul><li><p>Before the changes, no dispatcher was specified. All code runs in the default context of <code>runBlocking</code>. This meant that tasks such as <code>fetchBookFromApi</code> run on the same thread as <code>runBlocking</code>. This can be problematic if you are doing intensive tasks, such as API calls or database access, because they might block the main thread.</p></li><li><p>By implementing <code>Dispatchers.IO</code>, we take advantage of the fact that it is designed for I/O tasks, such as network calls. This ensures that these tasks are executed in a thread optimized for operations of this type, without blocking the main thread.</p></li><li><p>So, we clearly separate the network tasks (in <code>Dispatchers.IO</code>) from the UI-related tasks (in <code>Dispatchers.Main</code>).</p></li></ul></li><li><p><strong>Cancellation of coroutines</strong>:</p><ul><li><p>Previously, we cancelled manually with <code>job.cancelAndJoin()</code>. This stops the specific coroutine and waits for all resources to be released. This is effective but requires you to control each job individually. Switching to <code>scope.cancel()</code> stops all coroutines associated with <code>CoroutineScope</code>. This is cleaner and more scalable, especially if you have multiple related tasks.</p></li></ul></li><li><p><strong>Separation of contexts</strong>:</p><ul><li><p>Initially, there was no explicit separation of contexts. All operations, such as API calls and result handling, are executed in the same thread. With the changes, the contexts were separated clearly, as we can see:</p><pre><code><strong>withContext(Dispatchers.IO) { repository.fetchBookFromApi(bookId)
}</strong></code></pre></li></ul></li></ul><p>Networking code (API call) runs in <code>Dispatchers.IO</code>, while the rest of the code (e.g., printing to the console) can run in <code>Dispatchers.Main</code>. This improves performance and keeps the UI smooth.</p><p>In summary, the final code represents a more mature and professional implementation of coroutine handling in Kotlin, where each operation is executed in the most appropriate context: the UI in the main dispatcher and network operations in the I/O dispatcher. This approach not only optimizes the use of system resources but also provides a clearer and more maintainable structure, where responsibilities are well defined and separated. Operation cancellation is handled more robustly through a shared scope, allowing for efficient coroutine life cycle management.</p><p>Furthermore, this implementation pattern better reflects real-world practices in app development, especially on Android, where it is crucial to properly handle execution contexts and component life cycles. By using <code>CoroutineScope</code> defined at the class level along with specific dispatchers, you achieve more predictable and maintainable code, which can scale better as your app grows and becomes more complex.</p><div><hr></div><h1>Summary</h1><p>In this chapter, we delved into Kotlin coroutines, one of the most transformative features of the language for handling asynchronous programming. Here is a summary of what we&#8217;ve learned.</p><p>We started by demystifying coroutines through relatable analogies, such as a waiter in a restaurant. This helped visualize how coroutines enable concurrent, non-blocking tasks while keeping the main thread free for interactive operations.</p><p>We introduced coroutine builders such as <code>launch</code> and <code>runBlocking</code>, along with suspending functions such as <code>delay</code>. Through practical examples such as &#8220;<code>Hello World</code>&#8220; and the restaurant scenario, we showcased how to create and manage coroutines effectively.</p><p>We explored the critical concepts of setting time limits (<code>withTimeout</code>) and manually canceling tasks (<code>job.cancelAndJoin</code>). These mechanisms ensure efficient resource usage and help handle long-running tasks gracefully, as seen in the book-fetching example.</p><p>Finally, we covered how <code>CoroutineScope</code> and dispatchers are used to manage execution contexts and life cycles. We demonstrated how separating contexts (e.g., <code>Dispatchers.IO</code> for network calls and <code>Dispatchers.Main</code> for UI updates) improves application performance and maintainability. We also highlighted the importance of structured concurrency, enabling efficient cancellation and life cycle management for related tasks.</p><p>This chapter provided a robust foundation for understanding and applying Kotlin coroutines, a game-changing feature that simplifies asynchronous programming. By transitioning from basic examples to real-world scenarios, you now understand how coroutines enhance concurrency, improve code readability, and make resource management more efficient. These concepts are critical for developing scalable and responsive applications, whether for Android, server-side, or other Kotlin-based projects.</p><div><hr></div><p>If you want to dig deeper into the mechanics of moving from Java to Kotlin&#8212;writing <strong>idiomatic Kotlin</strong>, handling <strong>null safety</strong>, using <strong>coroutines</strong> for concurrency, and taking advantage of features like <strong>extension functions</strong> and <strong>DSLs</strong>&#8212;check out <em><strong><a href="https://www.packtpub.com/en-us/product/kotlin-for-java-developers-9781835884836">Kotlin for Java Developers</a></strong></em> by <strong>Jos&#233; Dimas Luj&#225;n Castillo</strong> and <strong>Ron Veen</strong> (Packt, Oct 2025). Written for experienced Java developers, it teaches Kotlin by mapping concepts directly to familiar Java constructs and then goes further into interoperability, generics, data and sealed classes, coroutines and flows, and DSL design&#8212;across backend, Android, and cross-platform development.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.packtpub.com/en-us/product/kotlin-for-java-developers-9781835884836" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BDJC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe245f2-991b-46bf-905b-bff991dc8e14_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!BDJC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe245f2-991b-46bf-905b-bff991dc8e14_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!BDJC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe245f2-991b-46bf-905b-bff991dc8e14_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!BDJC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe245f2-991b-46bf-905b-bff991dc8e14_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BDJC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe245f2-991b-46bf-905b-bff991dc8e14_2250x2775" width="336" height="414.46153846153845" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/abe245f2-991b-46bf-905b-bff991dc8e14_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:336,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Kotlin for Java Developers&quot;,&quot;title&quot;:&quot;Kotlin for Java Developers&quot;,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.packtpub.com/en-us/product/kotlin-for-java-developers-9781835884836&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Kotlin for Java Developers" title="Kotlin for Java Developers" srcset="https://substackcdn.com/image/fetch/$s_!BDJC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe245f2-991b-46bf-905b-bff991dc8e14_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!BDJC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe245f2-991b-46bf-905b-bff991dc8e14_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!BDJC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe245f2-991b-46bf-905b-bff991dc8e14_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!BDJC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe245f2-991b-46bf-905b-bff991dc8e14_2250x2775 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Go + Eino ADK Quickstart: Master Core AI Agent Design Patterns ]]></title><description><![CDATA[Build composable, interruptible multi-agent workflows in Go with Eino&#8217;s unified Agent abstraction&#8212;ReAct, WorkflowAgents, Supervisor, Plan-Execute-Replan, and DeepAgents.]]></description><link>https://deepengineering.substack.com/p/go-eino-adk-quickstart-master-core</link><guid isPermaLink="false">https://deepengineering.substack.com/p/go-eino-adk-quickstart-master-core</guid><dc:creator><![CDATA[Pandeng Li]]></dc:creator><pubDate>Fri, 30 Jan 2026 13:31:43 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/86222d3c-2ccb-48a5-9c2a-4402ed429f02_800x533.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>With thanks to AI Engineer <strong><a href="https://www.linkedin.com/in/gerald-parker-b7948050/">Gerald Parker</a></strong> for his technical review.</em></p></blockquote><p>Eino ADK, pronounced &#8220;I know&#8221;, is a multi-agent development framework designed for Go, developed and hardened in real-world use at ByteDance. Its design philosophy is &#8220;keep simple things simple, and make complex things possible&#8221;. Open-sourced at the start of 2025, Eino&#8217;s promise to Go developers is they can focus on implementing business logic without worrying about underlying technical complexity.</p><p>In this article co-written with the team behind Eino, we will discuss:</p><ul><li><p>What Eino ADK is</p></li></ul><ul><li><p>Core agent patterns in Eino along with real use cases</p></li></ul><ul><li><p>Example code for building a simple project manager agent</p></li></ul><h1>Introduction to Eino ADK</h1><p>Agents are quickly becoming the mainstream way to deploy LLMs, from intelligent customer service to automated office work. With them, the following pain points are emerging:</p><ul><li><p><strong>LLMs are not bridged well with business systems</strong>, resulting in agents that can only engage in &#8220;empty talk.&#8221;</p></li></ul><ul><li><p><strong>Lack of state management</strong> causes agents to frequently &#8220;forget&#8221; when performing tasks.</p></li></ul><ul><li><p><strong>Complex interactive processes</strong> increase development difficulty even further.</p></li></ul><p>Eino ADK was created to provide Go developers with a complete, flexible, and powerful agent development framework that addresses these core challenges head-on.</p><blockquote><h2>Recap: What is an Agent?</h2><p>You can think of an agent as an independent, intelligent entity that can understand instructions, perform tasks, and provide responses &#8211; capable of autonomous learning, adaptation, and decision-making. Its main functions include:</p><ul><li><p><strong>Reasoning:</strong> An agent can analyze data, identify patterns, and use logic and available information to draw conclusions, make inferences, and solve problems.</p></li><li><p><strong>Action:</strong> An agent takes actions or executes tasks to achieve goals based on decisions, plans, or external inputs.</p></li><li><p><strong>Observation:</strong> An agent autonomously collects relevant information (for example, through computer vision, natural language processing, or sensor data analysis) to understand the context and lay the foundation for informed decision-making.</p></li><li><p><strong>Planning:</strong> An agent can determine necessary steps, evaluate potential actions, and select the best course of action based on available information and expected outcomes.</p></li><li><p><strong>Collaboration:</strong> An agent can effectively work together with others (human or other agents) in complex and dynamic environments.</p></li></ul></blockquote><p><strong>Any scenario that requires interaction with a LLM can be abstracted as an agent.</strong> For example:</p><ul><li><p>An agent for querying weather information</p></li></ul><ul><li><p>An agent for booking meetings</p></li></ul><ul><li><p>An agent capable of answering questions in a specific domain</p></li></ul><h2>What is Eino ADK?</h2><p><a href="https://github.com/cloudwego/eino">Eino ADK</a> is a complete system for developing intelligent agents. It breaks down complex AI applications into independent, composable intelligent agent units, allowing developers to build agentic systems the way they would assemble Lego blocks.</p><p>Its features include:</p><ul><li><p><strong>Less glue code:</strong> Unified interfaces and event streams make complex task decomposition more natural.</p></li></ul><ul><li><p><strong>Rapid orchestration:</strong> Pre-built paradigms + workflows allow you to build pipelines in minutes.</p></li></ul><ul><li><p><strong>More controllable:</strong> Interruptible, resumable, and auditable, making the agent collaboration process &#8220;visible.&#8221;</p></li></ul><p>Eino achieves this through unified abstract interfaces, flexible composition patterns, and powerful collaboration mechanisms.</p><h2>The Core: Unified Agent Abstraction</h2><p>The core of ADK is a concise and powerful agent interface:</p><pre><code><strong>type Agent interface {
    Name(ctx context.Context) string
    Description(ctx context.Context) string
    Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent]
}</strong></code></pre><p>Each agent has:</p><ul><li><p>A clear identity (Name)</p></li></ul><ul><li><p>A clear responsibility (Description)</p></li></ul><ul><li><p>A standardized execution method (Run)</p></li></ul><p>This provides a basis for discovery and invocation between agents. Simple Q&amp;A robots or complex multi-step task processing systems can be implemented through this unified interface.</p><h1>Core Agent Patterns</h1><p>The following sections lay out the core patterns that make up Eino ADK. Each will be introduced, explained, and then brief skeleton code demonstrated.</p><h2>ChatModelAgent: The Brain</h2><p>ChatModelAgent is the most important pre-built component in Eino ADK. It encapsulates the interaction logic with LLMs and implements the classic <a href="https://react-lm.github.io/">ReAct</a> (Reason-Act-Observe) pattern. The process is as follows:</p><ol><li><p><strong>Reason</strong>: Call the LLM.</p></li></ol><ol start="2"><li><p><strong>Act</strong>: The LLM returns a tool call request.</p></li></ol><ol start="3"><li><p><strong>Act</strong>: ChatModelAgent executes the tool.</p></li></ol><ol start="4"><li><p><strong>Observe</strong>: The tool&#8217;s result is returned to the LLM, which continues to generate based on the previous context until the model determines that no more tool calls are needed.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a7vJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee46f9d4-fc00-4186-bf2b-f7502fc737e7_645x576.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a7vJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee46f9d4-fc00-4186-bf2b-f7502fc737e7_645x576.png 424w, https://substackcdn.com/image/fetch/$s_!a7vJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee46f9d4-fc00-4186-bf2b-f7502fc737e7_645x576.png 848w, https://substackcdn.com/image/fetch/$s_!a7vJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee46f9d4-fc00-4186-bf2b-f7502fc737e7_645x576.png 1272w, https://substackcdn.com/image/fetch/$s_!a7vJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee46f9d4-fc00-4186-bf2b-f7502fc737e7_645x576.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!a7vJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee46f9d4-fc00-4186-bf2b-f7502fc737e7_645x576.png" width="645" height="576" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee46f9d4-fc00-4186-bf2b-f7502fc737e7_645x576.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:576,&quot;width&quot;:645,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A diagram of a chat model\n\nAI-generated content may be incorrect.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A diagram of a chat model

AI-generated content may be incorrect." title="A diagram of a chat model

AI-generated content may be incorrect." srcset="https://substackcdn.com/image/fetch/$s_!a7vJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee46f9d4-fc00-4186-bf2b-f7502fc737e7_645x576.png 424w, https://substackcdn.com/image/fetch/$s_!a7vJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee46f9d4-fc00-4186-bf2b-f7502fc737e7_645x576.png 848w, https://substackcdn.com/image/fetch/$s_!a7vJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee46f9d4-fc00-4186-bf2b-f7502fc737e7_645x576.png 1272w, https://substackcdn.com/image/fetch/$s_!a7vJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee46f9d4-fc00-4186-bf2b-f7502fc737e7_645x576.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The core of the ReAct pattern is a closed loop of <strong>Reason &#8594; Act &#8594; Observe &#8594; Reason Again,</strong> which addresses the pain points of traditional agents that either act blindly or have a disconnect between reasoning and action.</p><p>Here are some possible practical scenarios:</p><ul><li><p><strong>Generating a market analysis report:</strong></p><ul><li><p><strong>Reason-1:</strong> To assess the potential of a sector, information on policy support, industry growth rate, profitability of leading companies, and supply chain bottlenecks is needed.</p></li><li><p><strong>Action-1:</strong> Call an API to get overall financial data for the industry.</p></li><li><p><strong>Observe-1</strong>: Analyze the data.</p></li><li><p><strong>Reason-2:</strong> Determine that the industry has high growth and policy endorsement, but rising upstream prices may squeeze downstream profits. Decide that further verification is needed.</p></li><li><p><strong>Act-2:</strong> Call an API to get detailed data on supply and demand, industry research reports, and so on.</p></li><li><p><strong>Observe-2</strong>: Analyze the data.</p></li><li><p><strong>Reason-3:</strong> Integrate the conclusions to generate an analysis report with key data sources.</p></li></ul></li></ul><blockquote><p>Using the ReAct pattern avoids information overload caused by collecting all the information at once. It focuses on core issues through gradual reasoning and uses data to verify thoughts rather than relying on intuition. The process is also <strong>explainable</strong>, improving the accuracy of the generated report.</p></blockquote><ul><li><p><strong>IT fault operations and maintenance:</strong></p><ul><li><p><strong>Reason-1:</strong> Identify common causes of failures, such as &#8220;CPU overload, insufficient memory, full disk, or service crash.&#8221; Basic monitoring data needs to be checked first.</p></li><li><p><strong>Act-1:</strong> Call the &#8220;Monitoring System API&#8221; to query server metrics.</p></li><li><p><strong>Observe-1</strong>: Analyze the server metrics.</p></li><li><p><strong>Reason-2:</strong> Determine the main cause. For example, if CPU utilization is abnormal, investigate which processes have high CPU usage.</p></li><li><p><strong>Act-2:</strong> Use a &#8220;Process Management Tool&#8221; to check the top processes for any abnormal services.</p></li><li><p><strong>Observe-2</strong>: Discover that the logging service is abnormal, possibly due to &#8220;log file too large&#8221; or &#8220;configuration error.&#8221; Verify these conditions before planning the next step.</p></li><li><p><strong>Reason-3:</strong> Check the logging service&#8217;s configuration and log file size.</p></li><li><p><strong>Act-3:</strong> Execute a bash command.</p></li><li><p><strong>Observe-4</strong>: Find that the log file is too large, and the configuration does not enable rotation or set a maximum log size.</p></li><li><p><strong>Reason-4:</strong> Provide a feasible solution to the O&amp;M engineer: clean up the logs, modify the configuration to enable rotation, and restart the logging service and application.</p></li></ul></li></ul><blockquote><p>The ReAct pattern gradually narrows down the problem scope, avoiding blind operations. Each step is well-founded, making it easy for O&amp;M engineers to perform secondary verification before implementing solutions, and providing a basis for subsequent reviews and preventative measures.</p></blockquote><p>ChatModelAgent leverages the capabilities of LLMs for reasoning, understanding natural language, making decisions, generating responses, and interacting with tools, <strong>acting as the &#8220;thinking&#8221; part of an intelligent agent application.</strong></p><p>Here is skeleton code for a ChatModelAgent with ReAct capabilities:</p><pre><code><strong>import "github.com/cloudwego/eino/adk"

// Create a ReAct ChatModelAgent with multiple tools
chatAgent := adk.NewChatModelAgent(ctx, &amp;adk.ChatModelAgentConfig{
    Name: "intelligent_assistant",
    Description: "An intelligent assistant capable of using multiple tools to solve complex problems",
    Instruction: "You are a professional assistant who can use the tools provided to help users solve problems",
    Model: openaiModel,
    ToolsConfig: adk.ToolsConfig{
        Tools: []tool.BaseTool{
            searchTool,
            calculatorTool,
            weatherTool,
        },
    },
})</strong></code></pre><h2>WorkflowAgents: The Sophisticated Pipeline</h2><p>Eino ADK provides the WorkflowAgents pattern, designed to coordinate the execution flow of sub-agents. It manages the running of agents through predefined logic to produce <strong>a deterministic execution process</strong>, helping to achieve a predictable and controllable output.</p><p>You can arrange and combine the following patterns as needed:</p><ul><li><p>SequentialAgent</p></li></ul><ul><li><p>ParallelAgent</p></li></ul><ul><li><p>LoopAgent</p></li></ul><p>And combine them with ChatModelAgent to construct a complete workflow pipeline. Here are descriptions of each agent in detail:</p><ul><li><p><strong>SequentialAgent:</strong> Executes the registered agents in the configuration in sequence once, and then ends. The operation follows these principles:</p><ul><li><p><strong>Linear execution:</strong> Strictly executes in the order of the SubAgents array.</p></li><li><p><strong>Passing of run results:</strong> Each agent in the configuration can get the complete input of the SequentialAgent and the output of the preceding agent.</p></li><li><p><strong>Support for early exit:</strong> If any sub-agent produces an exit/interrupt action, the entire Sequential process will be terminated immediately.</p></li></ul></li><li><p>Possible practical scenarios include:</p><ul><li><p><strong>Data ETL:</strong> ExtractAgent (extracts order data from MySQL) &#8594; TransformAgent (cleans null values, formats dates) &#8594; LoadAgent (loads into a data warehouse)</p></li><li><p><strong>CI/CD pipeline:</strong> CodeCloneAgent (pulls code from a code repository) &#8594; UnitTestAgent (runs unit tests, returns an error and analysis report when a test case fails) &#8594; CompileAgent (compiles the code) &#8594; DeployAgent (deploys to the target environment)</p></li></ul></li></ul><p>Skeleton code for a SequentialAgent:</p><pre><code><strong>import "github.com/cloudwego/eino/adk"

// Execute in sequence: create a research plan -&gt; search for materials -&gt; write a report
sequential := adk.NewSequentialAgent(ctx, &amp;adk.SequentialAgentConfig{
    Name: "research_pipeline",
    SubAgents: []adk.Agent{
        planAgent,   // Create a research plan
        searchAgent, // Search for materials
        writeAgent,  // Write a report
    },
})</strong></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!D9ZQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F475f76ec-452c-4886-8925-1d387e2a3ba6_690x408.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!D9ZQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F475f76ec-452c-4886-8925-1d387e2a3ba6_690x408.png 424w, https://substackcdn.com/image/fetch/$s_!D9ZQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F475f76ec-452c-4886-8925-1d387e2a3ba6_690x408.png 848w, https://substackcdn.com/image/fetch/$s_!D9ZQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F475f76ec-452c-4886-8925-1d387e2a3ba6_690x408.png 1272w, https://substackcdn.com/image/fetch/$s_!D9ZQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F475f76ec-452c-4886-8925-1d387e2a3ba6_690x408.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!D9ZQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F475f76ec-452c-4886-8925-1d387e2a3ba6_690x408.png" width="690" height="408" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/475f76ec-452c-4886-8925-1d387e2a3ba6_690x408.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:408,&quot;width&quot;:690,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A diagram of a process\n\nAI-generated content may be incorrect.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A diagram of a process

AI-generated content may be incorrect." title="A diagram of a process

AI-generated content may be incorrect." srcset="https://substackcdn.com/image/fetch/$s_!D9ZQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F475f76ec-452c-4886-8925-1d387e2a3ba6_690x408.png 424w, https://substackcdn.com/image/fetch/$s_!D9ZQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F475f76ec-452c-4886-8925-1d387e2a3ba6_690x408.png 848w, https://substackcdn.com/image/fetch/$s_!D9ZQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F475f76ec-452c-4886-8925-1d387e2a3ba6_690x408.png 1272w, https://substackcdn.com/image/fetch/$s_!D9ZQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F475f76ec-452c-4886-8925-1d387e2a3ba6_690x408.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p><strong>ParallelAgent:</strong> Executes the registered agents in the configuration concurrently, and ends after all agents have finished executing. The operation follows these principles:</p><ul><li><p><strong>Concurrent execution:</strong> All sub-agents start at the same time and execute in parallel in separate goroutines.</p></li><li><p><strong>Shared input:</strong> All sub-agents receive the same initial input as when the ParallelAgent was called.</p></li><li><p><strong>Waiting and result aggregation:</strong> Internally uses sync.WaitGroup to wait for all sub-agents to complete, collects the execution results of all sub-agents, and outputs them to the AsyncIterator in the order they are received.</p></li></ul></li><li><p>Possible practical scenarios include:</p><ul><li><p><strong>Multi-source data collection:</strong> MySQLCollector (collects user table) + PostgreSQLCollector (collects order table) + MongoDBCollector (collects product reviews)</p></li><li><p><strong>Multi-channel push:</strong> SocialPushAgent (pushes to social media account) + SMSPushAgent (sends SMS) + AppPushAgent (pushes to app)</p></li></ul></li></ul><p>Skeleton code for a ParallelAgent:</p><pre><code><strong>import "github.com/cloudwego/eino/adk"

// Concurrently execute sentiment analysis + keyword extraction + content summarization
parallel := adk.NewParallelAgent(ctx, &amp;adk.ParallelAgentConfig{
    Name: "multi_analysis",
    SubAgents: []adk.Agent{
        sentimentAgent, // Sentiment analysis
        keywordAgent,   // Keyword extraction
        summaryAgent,   // Content summarization
    },
})</strong></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Riw0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffde7a2c9-cd48-411d-8760-6b764ff5e93f_783x387.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Riw0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffde7a2c9-cd48-411d-8760-6b764ff5e93f_783x387.png 424w, https://substackcdn.com/image/fetch/$s_!Riw0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffde7a2c9-cd48-411d-8760-6b764ff5e93f_783x387.png 848w, https://substackcdn.com/image/fetch/$s_!Riw0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffde7a2c9-cd48-411d-8760-6b764ff5e93f_783x387.png 1272w, https://substackcdn.com/image/fetch/$s_!Riw0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffde7a2c9-cd48-411d-8760-6b764ff5e93f_783x387.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Riw0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffde7a2c9-cd48-411d-8760-6b764ff5e93f_783x387.png" width="783" height="387" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fde7a2c9-cd48-411d-8760-6b764ff5e93f_783x387.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:387,&quot;width&quot;:783,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A diagram of a parallel controller\n\nAI-generated content may be incorrect.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A diagram of a parallel controller

AI-generated content may be incorrect." title="A diagram of a parallel controller

AI-generated content may be incorrect." srcset="https://substackcdn.com/image/fetch/$s_!Riw0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffde7a2c9-cd48-411d-8760-6b764ff5e93f_783x387.png 424w, https://substackcdn.com/image/fetch/$s_!Riw0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffde7a2c9-cd48-411d-8760-6b764ff5e93f_783x387.png 848w, https://substackcdn.com/image/fetch/$s_!Riw0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffde7a2c9-cd48-411d-8760-6b764ff5e93f_783x387.png 1272w, https://substackcdn.com/image/fetch/$s_!Riw0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffde7a2c9-cd48-411d-8760-6b764ff5e93f_783x387.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p><strong>LoopAgent:</strong> Executes the registered agents in the configuration in sequence and loops multiple times. The operation follows these principles:</p><ul><li><p><strong>Loop execution:</strong> Repeatedly executes the SubAgents sequence, with each loop being a complete Sequential execution process.</p></li><li><p><strong>Accumulation of run results:</strong> The results of each iteration are accumulated, and the input of subsequent iterations can access all historical information.</p></li><li><p><strong>Conditional exit:</strong> Supports terminating the loop by outputting an event containing an ExitAction or reaching the maximum number of iterations. Configuring MaxIterations=0 means an infinite loop.</p></li></ul></li><li><p>Possible practical scenarios include:</p><ul><li><p><strong>Data Synchronization:</strong> CheckUpdateAgent (checks for incremental changes in the source database) &#8594; IncrementalSyncAgent (synchronizes incremental data) &#8594; VerifySyncAgent (verifies consistency)</p></li><li><p><strong>Pressure Testing:</strong> StartClientAgent (starts a test client) &#8594; SendRequestsAgent (sends requests) &#8594; CollectMetricsAgent (collects performance metrics)</p></li></ul></li></ul><p>Skeleton code for a LoopAgent:</p><pre><code><strong>import "github.com/cloudwego/eino/adk"

// Loop 5 times, with the sequence each time being: analyze the current state -&gt; propose an improvement plan -&gt; verify the improvement effect
loop := adk.NewLoopAgent(ctx, &amp;adk.LoopAgentConfig{
    Name: "iterative_optimization",
    SubAgents: []adk.Agent{
        analyzeAgent,   // Analyze the current state
        improveAgent,   // Propose an improvement plan
        validateAgent,  // Verify the improvement effect
    },
    MaxIterations: 5,
})</strong></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wIiS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84652a37-a337-468e-ba97-3fbb28b0c619_750x381.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wIiS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84652a37-a337-468e-ba97-3fbb28b0c619_750x381.png 424w, https://substackcdn.com/image/fetch/$s_!wIiS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84652a37-a337-468e-ba97-3fbb28b0c619_750x381.png 848w, https://substackcdn.com/image/fetch/$s_!wIiS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84652a37-a337-468e-ba97-3fbb28b0c619_750x381.png 1272w, https://substackcdn.com/image/fetch/$s_!wIiS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84652a37-a337-468e-ba97-3fbb28b0c619_750x381.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wIiS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84652a37-a337-468e-ba97-3fbb28b0c619_750x381.png" width="750" height="381" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/84652a37-a337-468e-ba97-3fbb28b0c619_750x381.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:381,&quot;width&quot;:750,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A diagram of a loop controller\n\nAI-generated content may be incorrect.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A diagram of a loop controller

AI-generated content may be incorrect." title="A diagram of a loop controller

AI-generated content may be incorrect." srcset="https://substackcdn.com/image/fetch/$s_!wIiS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84652a37-a337-468e-ba97-3fbb28b0c619_750x381.png 424w, https://substackcdn.com/image/fetch/$s_!wIiS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84652a37-a337-468e-ba97-3fbb28b0c619_750x381.png 848w, https://substackcdn.com/image/fetch/$s_!wIiS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84652a37-a337-468e-ba97-3fbb28b0c619_750x381.png 1272w, https://substackcdn.com/image/fetch/$s_!wIiS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84652a37-a337-468e-ba97-3fbb28b0c619_750x381.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Pre-built Multi-Agent Paradigms</h1><p>Eino ADK provides <strong>three pre-built multi-agent paradigms</strong>:</p><ul><li><p><strong>The Supervisor pattern</strong> for centralized coordination</p></li></ul><ul><li><p><strong>The Plan-Execute pattern</strong> for structured problem solving</p></li></ul><ul><li><p><strong>The DeepAgents pattern</strong> for complex workflows that require multi-step, multi-role collaboration</p></li></ul><p>Developers can use them out of the box without having to design collaboration logic from scratch. The following sections lay out the patterns along with skeleton code.</p><h3>Supervisor Pattern</h3><p>The Supervisor agent is a centralized multi-agent collaboration pattern, designed to provide a solution for the general scenario of centralized decision-making and distributed execution.</p><p>It consists of a Supervisor agent and multiple sub-agents, where:</p><ul><li><p><strong>The Supervisor agent</strong> is responsible for task allocation, summarizing the results after the sub-agents are completed, and making the next decision.</p></li></ul><ul><li><p><strong>The sub-agents</strong> focus on executing specific tasks and automatically return control of the task to the Supervisor after completion.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!z5bs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed5f32d-cc31-4a73-93c8-3781106050fd_741x429.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z5bs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed5f32d-cc31-4a73-93c8-3781106050fd_741x429.png 424w, https://substackcdn.com/image/fetch/$s_!z5bs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed5f32d-cc31-4a73-93c8-3781106050fd_741x429.png 848w, https://substackcdn.com/image/fetch/$s_!z5bs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed5f32d-cc31-4a73-93c8-3781106050fd_741x429.png 1272w, https://substackcdn.com/image/fetch/$s_!z5bs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed5f32d-cc31-4a73-93c8-3781106050fd_741x429.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!z5bs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed5f32d-cc31-4a73-93c8-3781106050fd_741x429.png" width="741" height="429" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6ed5f32d-cc31-4a73-93c8-3781106050fd_741x429.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:429,&quot;width&quot;:741,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A diagram of a mission\n\nAI-generated content may be incorrect.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A diagram of a mission

AI-generated content may be incorrect." title="A diagram of a mission

AI-generated content may be incorrect." srcset="https://substackcdn.com/image/fetch/$s_!z5bs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed5f32d-cc31-4a73-93c8-3781106050fd_741x429.png 424w, https://substackcdn.com/image/fetch/$s_!z5bs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed5f32d-cc31-4a73-93c8-3781106050fd_741x429.png 848w, https://substackcdn.com/image/fetch/$s_!z5bs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed5f32d-cc31-4a73-93c8-3781106050fd_741x429.png 1272w, https://substackcdn.com/image/fetch/$s_!z5bs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ed5f32d-cc31-4a73-93c8-3781106050fd_741x429.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The Supervisor pattern has the following characteristics:</p><ul><li><p><strong>Centralized control:</strong> The Supervisor uniformly manages the sub-agents and can dynamically adjust task allocation based on the input and the execution results of the sub-agents.</p></li></ul><ul><li><p><strong>Deterministic callback:</strong> After a sub-agent has finished executing, it will return the running result to the Supervisor agent, avoiding interruption of the collaboration process.</p></li></ul><ul><li><p><strong>Loosely coupled extension:</strong> Sub-agents can be developed, tested, and replaced independently, which is useful for expansion and maintenance.</p></li></ul><p>This hierarchical structure of the Supervisor pattern is suitable for scenarios that <strong>dynamically coordinate multiple specialized agents to complete complex tasks</strong>, such as:</p><ul><li><p><strong>Research project management:</strong> The Supervisor assigns research, experimentation, and report writing tasks to different sub-agents.</p></li></ul><ul><li><p><strong>Customer service processes:</strong> The Supervisor assigns tasks to technical support, after-sales, and sales sub-agents, based on the type of user problem.</p></li></ul><p>Skeleton code:</p><pre><code>import "github.com/cloudwego/eino/adk/prebuilt/supervisor"

// Research project management: create a multi-agent in supervisor mode
// Contains three sub-agents: research, experimentation, and report
supervisor, err := supervisor.New(ctx, &amp;supervisor.Config{
    SupervisorAgent: supervisorAgent,
    SubAgents: []adk.Agent{
        researchAgent,
        experimentationAgent,
        reportAgent,
    },
})</code></pre><h2>Plan-Execute-Replan Pattern</h2><p>The Plan-Execute-Replan agent is a multi-agent collaboration pattern based on the &#8220;plan-execute-reflect&#8221; paradigm (refer to the 2023 paper <strong><a href="https://arxiv.org/abs/2305.04091">Plan-and-Solve Prompting</a> </strong>by Wang L. et al). Through the collaborative work of three core intelligent agents, it achieves structured planning of tasks, tool call execution, progress evaluation, and dynamic replanning:</p><ul><li><p><strong>Planner:</strong> Generates a structured initial task plan with detailed steps based on the user&#8217;s goals.</p></li></ul><ul><li><p><strong>Executor:</strong> Executes the first step in the current plan.</p></li></ul><ul><li><p><strong>Replanner:</strong> Evaluates the execution progress and decides whether to revise the plan and continue to have the Executor run it, or to end the task.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CQOC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa864e87b-ff61-4bf5-a6be-5ce073b36ecf_771x840.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CQOC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa864e87b-ff61-4bf5-a6be-5ce073b36ecf_771x840.png 424w, https://substackcdn.com/image/fetch/$s_!CQOC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa864e87b-ff61-4bf5-a6be-5ce073b36ecf_771x840.png 848w, https://substackcdn.com/image/fetch/$s_!CQOC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa864e87b-ff61-4bf5-a6be-5ce073b36ecf_771x840.png 1272w, https://substackcdn.com/image/fetch/$s_!CQOC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa864e87b-ff61-4bf5-a6be-5ce073b36ecf_771x840.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CQOC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa864e87b-ff61-4bf5-a6be-5ce073b36ecf_771x840.png" width="771" height="840" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a864e87b-ff61-4bf5-a6be-5ce073b36ecf_771x840.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:840,&quot;width&quot;:771,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A diagram of a plan\n\nAI-generated content may be incorrect.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A diagram of a plan

AI-generated content may be incorrect." title="A diagram of a plan

AI-generated content may be incorrect." srcset="https://substackcdn.com/image/fetch/$s_!CQOC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa864e87b-ff61-4bf5-a6be-5ce073b36ecf_771x840.png 424w, https://substackcdn.com/image/fetch/$s_!CQOC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa864e87b-ff61-4bf5-a6be-5ce073b36ecf_771x840.png 848w, https://substackcdn.com/image/fetch/$s_!CQOC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa864e87b-ff61-4bf5-a6be-5ce073b36ecf_771x840.png 1272w, https://substackcdn.com/image/fetch/$s_!CQOC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa864e87b-ff61-4bf5-a6be-5ce073b36ecf_771x840.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The Plan-Execute-Replan pattern has the following characteristics:</p><ul><li><p><strong>Clear hierarchical architecture:</strong> By breaking down the task into three stages &#8211;planning, execution, and reflection/replanning &#8211; a hierarchical cognitive process is formed, which embodies the closed-loop cognitive strategy of &#8220;think before you act, then adjust based on feedback.&#8221;</p></li></ul><ul><li><p><strong>Dynamic iterative optimization:</strong> The Replanner determines in real time whether the task is complete or needs to be adjusted based on the execution results and current progress, and supports dynamic replanning. This mechanism effectively solves the bottleneck of traditional single-planning &#8211; where there is difficulty coping with environmental changes and task uncertainty &#8211; and improves the robustness and flexibility of the system.</p></li></ul><ul><li><p><strong>Clear responsibilities and loose coupling:</strong> The Plan-Execute-Replan pattern consists of multiple intelligent agents working together, and supports independent development, testing, and replacement. The modular design is useful for expansion and maintenance and conforms to engineering best practices.</p></li></ul><ul><li><p><strong>Good extensibility:</strong> It does not rely on a specific language model, tool, or agent, which is convenient for integrating diverse external resources to meet the needs of different application scenarios.</p></li></ul><p>The &#8220;plan &#8594; execute &#8594; replan&#8221; closed-loop structure of the pattern is suitable for <strong>complex task scenarios that require multi-step reasoning, dynamic adjustment, and tool integration</strong>, such as:</p><ul><li><p><strong>Complex research analysis:</strong> Decompose research questions through planning, perform multiple rounds of data retrieval and calculation, and dynamically adjust research directions and hypotheses to improve the depth and accuracy of the analysis.</p></li></ul><ul><li><p><strong>Automated workflow management:</strong> Decompose complex business processes into structured steps, combine them with a variety of tools (such as database queries, API calls, and compute services) to execute them step-by-step, and dynamically optimize the process based on execution results.</p></li></ul><ul><li><p><strong>Multi-step problem-solving:</strong> Scenarios that require step-by-step reasoning and multi-tool collaboration, such as legal consultation, technical diagnosis, and strategy formulation, to ensure that each step of execution has feedback and adjustment.</p></li></ul><ul><li><p><strong>Intelligent assistant task execution:</strong> Supports intelligent assistants to plan task steps based on user goals, call external tools to complete specific operations, and adjust subsequent plans based on replanning and thinking combined with user feedback, improving the completeness and accuracy of task completion.</p></li></ul><p>Skeleton code:</p><pre><code><strong>import "github.com/cloudwego/eino/adk/prebuilt/planexecute"

// A research assistant in Plan-Execute mode
researchAssistant := planexecute.New(ctx, &amp;planexecute.Config{
    Planner: adk.NewChatModelAgent(ctx, &amp;adk.ChatModelAgentConfig{
        Name:        "research_planner",
        Instruction: "Create a detailed research plan, including literature research, data collection, analysis methods, etc.",
        Model:       gpt4Model,
    }),
    Executor: adk.NewChatModelAgent(ctx, &amp;adk.ChatModelAgentConfig{
        Name: "research_executor",
        ToolsConfig: adk.ToolsConfig{
            Tools: []tool.BaseTool{
                scholarSearchTool,
                dataAnalysisTool,
                citationTool,
            },
        },
    }),
    Replanner: replannerAgent,
})</strong></code></pre><h2>DeepAgents Pattern</h2><p>DeepAgents is a multi-agent pattern unified under the coordination of a MainAgent. The MainAgent leverages a ChatModel with tool-calling capabilities to operate through a ReAct workflow:</p><ul><li><p>It decomposes user goals into structured to-do items and records progress through WriteTodos.</p></li></ul><ul><li><p>It selects and invokes corresponding sub-agents to execute subtasks through the unified TaskTool interface; main/sub-agent contexts are isolated to prevent intermediate steps from contaminating the main workflow.</p></li></ul><ul><li><p>It aggregates results returned by various sub-agents; when necessary, it calls WriteTodos again to update progress or perform replanning until completion.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IgxE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5ed2ba3-c2f9-40ff-9327-80fa057a7ba3_747x579.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IgxE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5ed2ba3-c2f9-40ff-9327-80fa057a7ba3_747x579.png 424w, https://substackcdn.com/image/fetch/$s_!IgxE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5ed2ba3-c2f9-40ff-9327-80fa057a7ba3_747x579.png 848w, https://substackcdn.com/image/fetch/$s_!IgxE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5ed2ba3-c2f9-40ff-9327-80fa057a7ba3_747x579.png 1272w, https://substackcdn.com/image/fetch/$s_!IgxE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5ed2ba3-c2f9-40ff-9327-80fa057a7ba3_747x579.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IgxE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5ed2ba3-c2f9-40ff-9327-80fa057a7ba3_747x579.png" width="747" height="579" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c5ed2ba3-c2f9-40ff-9327-80fa057a7ba3_747x579.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:579,&quot;width&quot;:747,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A diagram of a task tool\n\nAI-generated content may be incorrect.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A diagram of a task tool

AI-generated content may be incorrect." title="A diagram of a task tool

AI-generated content may be incorrect." srcset="https://substackcdn.com/image/fetch/$s_!IgxE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5ed2ba3-c2f9-40ff-9327-80fa057a7ba3_747x579.png 424w, https://substackcdn.com/image/fetch/$s_!IgxE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5ed2ba3-c2f9-40ff-9327-80fa057a7ba3_747x579.png 848w, https://substackcdn.com/image/fetch/$s_!IgxE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5ed2ba3-c2f9-40ff-9327-80fa057a7ba3_747x579.png 1272w, https://substackcdn.com/image/fetch/$s_!IgxE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5ed2ba3-c2f9-40ff-9327-80fa057a7ba3_747x579.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The characteristics of the DeepAgents pattern include:</p><ul><li><p><strong>Enhanced task decomposition and progress management</strong>: WriteTodos creates clear subtasks and milestones, making complex goals decomposable and trackable.</p></li></ul><ul><li><p><strong>More robust context isolation</strong>: Sub-agents execute in &#8220;clean&#8221; contexts while the main agent only aggregates results, reducing interference from redundant reasoning chains and tool call traces on the main workflow.</p></li></ul><ul><li><p><strong>Unified delegation interface with easy extensibility</strong>: TaskTool abstracts all sub-agents and tool capabilities into a unified calling surface, facilitating the addition or replacement of specialized sub-agents.</p></li></ul><ul><li><p><strong>Flexible closed-loop of planning and execution</strong>: Planning functions as a tool that can be called on demand; for simple tasks, unnecessary planning can be skipped, reducing LLM call costs and time consumption.</p></li></ul><ul><li><p><strong>Boundaries and trade-offs</strong>: Over-decomposition increases call frequency and costs; it places higher demands on subtask division and prompt optimization, requiring models to possess stable tool-calling and planning capabilities.</p></li></ul><p>The core value of DeepAgent lies in automatically handling complex workflows that require multi-step, multi-role collaboration. It&#8217;s not just an executor of single functions but a &#8220;project manager&#8221; with deep thinking, planning, and dynamic adjustment capabilities.</p><p>Suitable application scenarios include:</p><ul><li><p><strong>Complex business processes with multi-role collaboration</strong>: Centered around R&amp;D, testing, release, legal, and operations roles, with centralized delegation of subtasks and unified aggregation; each stage sets gateways and rollback strategies, with visible progress and retry capabilities.</p></li></ul><ul><li><p><strong>Phased management of long workflows</strong>: Planning decomposes steps such as data cleaning, validation, lineage analysis, and quality inspection; sub-agents run in isolated contexts; when exceptions occur, only relevant stages are re-run, with unified reconciliation and aggregation of outputs.</p></li></ul><ul><li><p><strong>Execution environments requiring strict context isolation</strong>: A unified interface collects materials and requests, with TaskTool routing subtasks such as legal, risk control, and finance separately; boundaries between subtasks are clearly defined and mutually invisible, with auditable progress and traceability, and failures can be retried without affecting other stages.</p></li></ul><p>Skeleton code:</p><pre><code><strong>import "github.com/cloudwego/eino/adk/prebuilt/deep"

agent, err := deep.New(ctx, &amp;deep.Config{
    Name:       "deep-agent",
    ChatModel:  gpt4Model,
    SubAgents: []adk.Agent{
        LegalAgent,
        RiskControlAgent,
        FinanceAgent,
    },
    MaxIteration: 100,
})</strong></code></pre><h1>Example: Project Manager Intelligent Agent</h1><p>Now you have covered the basics, it&#8217;s time to create an agent. Install the latest version of Eino using the following command:</p><pre><code><strong>go get github.com/cloudwego/eino@latest</strong></code></pre><p>In this example, you&#8217;ll see how to build a Project Manager intelligent agent for a variety of scenarios. It uses the Supervisor pattern, and the functions of each agent are as follows:</p><ul><li><p><strong>ResearchAgent</strong>: Responsible for researching and generating feasible solutions, supporting interruption and receiving additional context information from users to improve the accuracy of research plan generation.</p></li></ul><ul><li><p><strong>CodeAgent</strong>: Uses knowledge base tools to recall relevant knowledge as a reference to generate high-quality code.</p></li></ul><ul><li><p><strong>ReviewAgent</strong>: Uses a sequential workflow to orchestrate three steps: problem analysis, evaluation generation, and evaluation verification to review research results/coding results and provide reasonable evaluations so project managers can make decisions.</p></li></ul><ul><li><p><strong>ProjectManagerAgent</strong>: Routes and coordinates multiple sub-intelligent agents responsible for different dimensions of work based on dynamic user input.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BhOJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5da31724-dd6d-4335-bd07-d9746c470002_903x396.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BhOJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5da31724-dd6d-4335-bd07-d9746c470002_903x396.png 424w, https://substackcdn.com/image/fetch/$s_!BhOJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5da31724-dd6d-4335-bd07-d9746c470002_903x396.png 848w, https://substackcdn.com/image/fetch/$s_!BhOJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5da31724-dd6d-4335-bd07-d9746c470002_903x396.png 1272w, https://substackcdn.com/image/fetch/$s_!BhOJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5da31724-dd6d-4335-bd07-d9746c470002_903x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BhOJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5da31724-dd6d-4335-bd07-d9746c470002_903x396.png" width="903" height="396" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5da31724-dd6d-4335-bd07-d9746c470002_903x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:396,&quot;width&quot;:903,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A diagram of a diagram\n\nAI-generated content may be incorrect.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A diagram of a diagram

AI-generated content may be incorrect." title="A diagram of a diagram

AI-generated content may be incorrect." srcset="https://substackcdn.com/image/fetch/$s_!BhOJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5da31724-dd6d-4335-bd07-d9746c470002_903x396.png 424w, https://substackcdn.com/image/fetch/$s_!BhOJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5da31724-dd6d-4335-bd07-d9746c470002_903x396.png 848w, https://substackcdn.com/image/fetch/$s_!BhOJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5da31724-dd6d-4335-bd07-d9746c470002_903x396.png 1272w, https://substackcdn.com/image/fetch/$s_!BhOJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5da31724-dd6d-4335-bd07-d9746c470002_903x396.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The design of this example covers most of the concepts introduced in the article.</p><p>The core code is as follows. The complete code can be found in the <a href="https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/integration-project-manager">source code</a> provided in the eino-examples project:</p><pre><code><strong>func main() {
    ctx := context.Background()

    // Init chat model for agents
    tcm, err := openai.NewChatModel(ctx, &amp;openai.ChatModelConfig{
        APIKey:  os.Getenv("OPENAI_API_KEY"),
        Model:   os.Getenv("OPENAI_MODEL"),
        BaseURL: os.Getenv("OPENAI_BASE_URL"),
        ByAzure: func() bool {
            return os.Getenv("OPENAI_BY_AZURE") == "true"
        }(),
    })
    if err != nil {
        log.Fatal(err)
    }

    // Init research agent
    researchAgent, err := agents.NewResearchAgent(ctx, tcm)
    if err != nil {
        log.Fatal(err)
    }

    // Init code agent
    codeAgent, err := agents.NewCodeAgent(ctx, tcm)
    if err != nil {
        log.Fatal(err)
    }

    // Init technical agent
    reviewAgent, err := agents.NewReviewAgent(ctx, tcm)
    if err != nil {
        log.Fatal(err)
    }

    // Init project manager agent
    s, err := agents.NewProjectManagerAgent(ctx, tcm)
    if err != nil {
        log.Fatal(err)
    }

    // Combine agents into ADK supervisor pattern
    // Supervisor: project manager
    // Sub-agents: researcher / coder / reviewer
    supervisorAgent, err := supervisor.New(ctx, &amp;supervisor.Config{
        Supervisor: s,
        SubAgents:  []adk.Agent{researchAgent, codeAgent, reviewAgent},
    })
    if err != nil {
        log.Fatal(err)
    }

    // Init Agent runner
    runner := adk.NewRunner(ctx, adk.RunnerConfig{
        Agent:           supervisorAgent,
        EnableStreaming: true,               // enable stream output
        CheckPointStore: newInMemoryStore(),  // enable checkpoint for interrupt &amp; resume
    })

    // Replace it with your own query
    query := "please generate a simple ai chat project with python."
    checkpointID := "1"

    // Start runner with a new checkpoint id
    iter := runner.Query(ctx, query, adk.WithCheckPointID(checkpointID))

    interrupted := false
    for {
        event, ok := iter.Next()
        if !ok {
            break
        }

        if event.Err != nil {
            log.Fatal(event.Err)
        }

        if event.Action != nil &amp;&amp; event.Action.Interrupted != nil {
            interrupted = true
        }

        prints.Event(event)
    }

    if !interrupted {
        return
    }

    // interrupt and ask for additional user context
    scanner := bufio.NewScanner(os.Stdin)
    fmt.Print("\ninput additional context for web search: ")
    scanner.Scan()
    fmt.Println()
    nInput := scanner.Text()

    // Resume by checkpoint id, with additional user context injection
    iter, err = runner.Resume(
        ctx,
        checkpointID,
        adk.WithToolOptions([]tool.Option{agents.WithNewInput(nInput)}),
    )
    if err != nil {
        log.Fatal(err)
    }

    for {
        event, ok := iter.Next()
        if !ok {
            break
        }

        if event.Err != nil {
            log.Fatal(event.Err)
        }

        prints.Event(event)
    }
}</strong></code></pre><p>If you stop and think about how you would build this example with regular tools, the advantages of ADK will become apparent:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7VWV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35db8b5-d8ee-4f48-b15c-b3f6e2e27ab1_742x636.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7VWV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35db8b5-d8ee-4f48-b15c-b3f6e2e27ab1_742x636.png 424w, https://substackcdn.com/image/fetch/$s_!7VWV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35db8b5-d8ee-4f48-b15c-b3f6e2e27ab1_742x636.png 848w, https://substackcdn.com/image/fetch/$s_!7VWV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35db8b5-d8ee-4f48-b15c-b3f6e2e27ab1_742x636.png 1272w, https://substackcdn.com/image/fetch/$s_!7VWV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35db8b5-d8ee-4f48-b15c-b3f6e2e27ab1_742x636.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7VWV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35db8b5-d8ee-4f48-b15c-b3f6e2e27ab1_742x636.png" width="742" height="636" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b35db8b5-d8ee-4f48-b15c-b3f6e2e27ab1_742x636.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:636,&quot;width&quot;:742,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:78840,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/186279844?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35db8b5-d8ee-4f48-b15c-b3f6e2e27ab1_742x636.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7VWV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35db8b5-d8ee-4f48-b15c-b3f6e2e27ab1_742x636.png 424w, https://substackcdn.com/image/fetch/$s_!7VWV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35db8b5-d8ee-4f48-b15c-b3f6e2e27ab1_742x636.png 848w, https://substackcdn.com/image/fetch/$s_!7VWV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35db8b5-d8ee-4f48-b15c-b3f6e2e27ab1_742x636.png 1272w, https://substackcdn.com/image/fetch/$s_!7VWV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35db8b5-d8ee-4f48-b15c-b3f6e2e27ab1_742x636.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Useful Resources</h1><p>Find out more about Eino using the following links:</p><ul><li><p><a href="https://www.cloudwego.io/zh/docs/eino/core_modules/eino_adk/">Read the docs</a></p></li></ul><ul><li><p><a href="https://github.com/cloudwego/eino/tree/main/adk">Browse source code on GitHub</a></p></li></ul><ul><li><p><a href="https://github.com/cloudwego/eino-examples/tree/main/adk">Explore examples on GitHub</a></p></li></ul><p>If you have questions about Eino, head to <a href="https://github.com/cloudwego/eino">GitHub</a> to fill out an issue.</p><p>If you want to get in touch with the Eino Team Lead, Li Pandeng, reach out to lipandeng [at] bytedance [dot] com.</p><p></p>]]></content:encoded></item><item><title><![CDATA[The Rise of the AI Architect]]></title><description><![CDATA[How AI architects can bridge AI and engineering to ship reliable decision-making systems]]></description><link>https://deepengineering.substack.com/p/the-rise-of-the-ai-architect</link><guid isPermaLink="false">https://deepengineering.substack.com/p/the-rise-of-the-ai-architect</guid><dc:creator><![CDATA[Richard D Avila]]></dc:creator><pubDate>Thu, 29 Jan 2026 06:47:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/120ba63b-e77a-4e9c-bfec-06e0652130da_800x533.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AI systems and technology are very quickly impacting so many areas of modern life. Whether it be a seemingly simple task, such as detecting a cat in an image, or the autonomous operation of a vehicle. There is also a shift toward greater acceptance of AI technologies into society. AI systems are primarily built into complex software. What makes AI-enabled systems unique is that they have, at their heart, the notion of decision making. This extended functionality puts new demands on the processes and guides for building these sorts of systems. </p><p>Architecting fundamentals can help navigate these complexities and the successful building and operation of these sorts of systems. Historically, an architect would aid in the building of a system and then would be able to sort of &#8220;walk away&#8221; or execute a new project. In this age of AI, this is simply not the case. The challenges of observability, integrated development, testing, the voice of the user, and their centrality to business outcomes place new demands on an architect.</p><h1>Challenges that impact AI enabled systems</h1><ul><li><p><strong>Complex integrations:</strong> For example, is the system going to be a hybrid cloud or on premise deployment? What dependencies exist on external software?</p></li><li><p><strong>High performance bar:</strong> the system is expected to be deployed to impact key business functions or activities that the organization shall depend upon.</p></li><li><p>Depending on the technology, using the AI/ML can incur significant costs.</p></li><li><p>If not well architected or instrumented, the system can be a challenge to troubleshoot.</p></li></ul><h1>What can architecture do to help?</h1><ul><li><p>By modeling and conducting robust conceptual designs, the unique perspective and aspects of the AI technology are identified.</p></li><li><p>The use of tactics and patterns partitions the system for better performance and meeting non-functional requirements.</p></li><li><p>Robust architecting helps keep a holistic view of the end to end system.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IPp_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bed346d-e43d-4a73-8131-8b83f4757109_1408x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IPp_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bed346d-e43d-4a73-8131-8b83f4757109_1408x768.png 424w, https://substackcdn.com/image/fetch/$s_!IPp_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bed346d-e43d-4a73-8131-8b83f4757109_1408x768.png 848w, https://substackcdn.com/image/fetch/$s_!IPp_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bed346d-e43d-4a73-8131-8b83f4757109_1408x768.png 1272w, https://substackcdn.com/image/fetch/$s_!IPp_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bed346d-e43d-4a73-8131-8b83f4757109_1408x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IPp_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bed346d-e43d-4a73-8131-8b83f4757109_1408x768.png" width="1408" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6bed346d-e43d-4a73-8131-8b83f4757109_1408x768.png&quot;,&quot;srcNoWatermark&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/65d3dfbf-b063-46f9-a5ab-d66b020e7c59_1408x768.png&quot;,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1885468,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/186053945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57f29466-c24a-4265-83e7-02814edfcd79_1408x768.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IPp_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bed346d-e43d-4a73-8131-8b83f4757109_1408x768.png 424w, https://substackcdn.com/image/fetch/$s_!IPp_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bed346d-e43d-4a73-8131-8b83f4757109_1408x768.png 848w, https://substackcdn.com/image/fetch/$s_!IPp_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bed346d-e43d-4a73-8131-8b83f4757109_1408x768.png 1272w, https://substackcdn.com/image/fetch/$s_!IPp_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bed346d-e43d-4a73-8131-8b83f4757109_1408x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 1: Architecting for Success - Transforming AI Complexity, Cost, and Performance Challenges into Scalable Solutions</figcaption></figure></div><h1>The rise of AI architects</h1><p>History has conclusively shown that as modern technology progresses, the practice of architecting also evolves. We are now witnessing the rise of the AI architect. The modern AI architect will be closer to operations and must help with managing the complexity of systems. These include:</p><ul><li><p>The architect is the principal approver for the completion of acceptance gates for going from an AI prototype to a production deployment. He ensures the concept has merit, evaluates the results of the prototype, guides design of the production model, evaluates the performance of prototypes, and finally accepts the production deployment.</p></li><li><p>They integrate and synthesize solutions to balance the data science, data engineering, software development, operations, and business teams.</p></li><li><p>The architect is the principal owner of the non-functional requirements and interfaces across the AI system.</p></li><li><p>They must be involved in the observability, where they need to understand what decisions the software is making.</p></li></ul><h1>From Idea to Production Life cycle</h1><p>As AI engineering is maturing, a notional process is emerging that takes an AI idea to production. The initial stage is discovery and evaluation. It looks to determine if the data, algorithms, and compute resources exist to make a system that will have impact. The next stage is prototyping; here one now adds complexity to the software, both by adding more production domain elements, to test how well the new system would work. It is at the end of the prototype phase where a key decision is made to determine if it is warranted to take the prototype to a production environment. </p><p>With the passage out of the prototyping phase, a controlled execution of the model is done, where it is observed and monitored to ensure it is working as expected and delivering value. With the monitoring phase complete, a controlled rollout of the system is done to production, with another layer of monitoring to ensure the systems it is now impacting still also function as expected. Finally, with these gates completed, a full deployment can be done and now the system goes into a monitor and evaluation phase. The evaluation is to see if the model needs to be adapted or retrained.</p><p>One key item is as the system is now in operation &#8211; a new set of metrics for system observability need to be captured. Below are some basic ones; this should not be considered exhaustive:</p><ol><li><p>Accuracy</p></li><li><p>Data throughput</p></li><li><p>Area under the Curve</p></li><li><p>Time for processing</p></li><li><p>Cost per transaction</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QOle!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f4087a5-b236-40b7-9db6-6b474aa57b3c_1408x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QOle!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f4087a5-b236-40b7-9db6-6b474aa57b3c_1408x768.png 424w, https://substackcdn.com/image/fetch/$s_!QOle!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f4087a5-b236-40b7-9db6-6b474aa57b3c_1408x768.png 848w, https://substackcdn.com/image/fetch/$s_!QOle!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f4087a5-b236-40b7-9db6-6b474aa57b3c_1408x768.png 1272w, https://substackcdn.com/image/fetch/$s_!QOle!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f4087a5-b236-40b7-9db6-6b474aa57b3c_1408x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QOle!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f4087a5-b236-40b7-9db6-6b474aa57b3c_1408x768.png" width="1408" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9f4087a5-b236-40b7-9db6-6b474aa57b3c_1408x768.png&quot;,&quot;srcNoWatermark&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8e8df155-fb3c-4e91-8c36-248280d2157b_1408x768.png&quot;,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1593480,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/186053945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e8df155-fb3c-4e91-8c36-248280d2157b_1408x768.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QOle!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f4087a5-b236-40b7-9db6-6b474aa57b3c_1408x768.png 424w, https://substackcdn.com/image/fetch/$s_!QOle!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f4087a5-b236-40b7-9db6-6b474aa57b3c_1408x768.png 848w, https://substackcdn.com/image/fetch/$s_!QOle!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f4087a5-b236-40b7-9db6-6b474aa57b3c_1408x768.png 1272w, https://substackcdn.com/image/fetch/$s_!QOle!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f4087a5-b236-40b7-9db6-6b474aa57b3c_1408x768.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 2: The AI Engineering Lifecycle - From Concept to Continuous Observability</figcaption></figure></div><p>How often these metrics should be reviewed really depends on how time dependent AI performance is to the enterprise. That said, these observability metrics should be reviewed frequently by the operations teams, and on a weekly basis or sooner by the other stakeholders, to see how AI model performance is impacting their areas of responsibilities.</p><h1>Specific areas an AI architect can influence</h1><p>As has been mentioned, the AI architect needs to have a solid understanding of, and be able to communicate in the language of, AI/ML and data science topics. This includes the perspective in understanding the role of non-functional requirements as applied to AI/ML systems. How to extend their knowledge for architecture modeling should incorporate systems where there is to be a sort of non-human decision making. They will also need to fortify and understand how users and stakeholders of the system are to be impacted, aided, and potential friction points. They will need to be close to guiding and evaluating the rapid prototyping in support of software development. The architect needs to consider interfaces and how their management will impact the decision making of the system. The use of AI/ML technologies inherently add a layer of complexity. The architect acts at his own peril with the notion that a simple AI application can readily be made in a less rigorous manner. Adding a layer of AI into almost any application raises the complexity significantly.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jkFh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a8e7a1d-78f2-4fe5-b982-3fc2155d5d68_1408x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jkFh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a8e7a1d-78f2-4fe5-b982-3fc2155d5d68_1408x768.png 424w, https://substackcdn.com/image/fetch/$s_!jkFh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a8e7a1d-78f2-4fe5-b982-3fc2155d5d68_1408x768.png 848w, https://substackcdn.com/image/fetch/$s_!jkFh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a8e7a1d-78f2-4fe5-b982-3fc2155d5d68_1408x768.png 1272w, https://substackcdn.com/image/fetch/$s_!jkFh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a8e7a1d-78f2-4fe5-b982-3fc2155d5d68_1408x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jkFh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a8e7a1d-78f2-4fe5-b982-3fc2155d5d68_1408x768.png" width="1408" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2a8e7a1d-78f2-4fe5-b982-3fc2155d5d68_1408x768.png&quot;,&quot;srcNoWatermark&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/acf66d0c-9121-49a3-bad5-16efe9a09a5f_1408x768.png&quot;,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1807524,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/186053945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facf66d0c-9121-49a3-bad5-16efe9a09a5f_1408x768.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jkFh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a8e7a1d-78f2-4fe5-b982-3fc2155d5d68_1408x768.png 424w, https://substackcdn.com/image/fetch/$s_!jkFh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a8e7a1d-78f2-4fe5-b982-3fc2155d5d68_1408x768.png 848w, https://substackcdn.com/image/fetch/$s_!jkFh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a8e7a1d-78f2-4fe5-b982-3fc2155d5d68_1408x768.png 1272w, https://substackcdn.com/image/fetch/$s_!jkFh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a8e7a1d-78f2-4fe5-b982-3fc2155d5d68_1408x768.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 3: The Central Nexus of AI Engineering - Orchestrating the Critical Intersections of Data, Development, Operations, and Compliance.</figcaption></figure></div><p>The AI architect must by necessity straddle AI/ML and software engineering. This is to ensure the architect can navigate the alphabet soup of techniques, algorithmic aspects, and technologies that exist. It is very easy for an AI architect to be overcome by the various other roles needed to deploy modern AI systems. In many enterprises, there are typically several functional teams: Data Team, Development Team, ML Ops, Legal/Compliance. Below are some of the major facets that the architect influences and needs to be involved with:</p><h3>Data Team</h3><p>The architect works with the data team to ensure that the product question can indeed be answered by the data that exists or is to be used. He is pivotal in levying requirements for the speed, format, and consistency checks that must be met.</p><h3>Development Team</h3><p>The architect works to ensure top-level designs, interfaces, and non-functional requirements are being met by the designs being created. He is more in a reviewer mode. He also aids in clarifying engineering requirements and providing guidance to address design questions and challenges.</p><h3>MLOps Team</h3><p>The architect, in this role, defines how well the production system is required to perform, and how the system is to be instrumented and monitored for correct execution. He will also define the canaries and fail-safe mechanisms to ensure issues with the AI components do not compromise the full production environment. The mechanisms for model and system updating are also laid out by the architect.</p><h3>Legal and Compliance Team</h3><p>In this capacity, the architect provides the technical insight, design guidance, and evidence that the AI system is meeting the legal and compliance needs of the enterprise. The architect also oversees re-design activities and troubleshooting to ensure legal and compliance aspects are indeed met.</p><h3>Incident Run Book</h3><p>There needs to exist a run book that the architect is a principal in its development. A run book should look to use a layered manner to address an error in the AI model or its interfaces. Clear mechanisms and architecture need to exist to be able to disengage the model from a production system, and put the production system into a nominal configuration. The run book should also provide for clear expected outputs of the different stages of the system &#8211; that is, it should be documented what &#8220;correct&#8221; looks like and why at each stage to enable quicker troubleshooting. The error handling within the system should also be readily indexed in the run book so that system, data, and model tracing can occur. Finally, the run book should also include clear traceability to software repositories, configuration information, and points of contact.</p><h1>Conclusion</h1><p>These are exciting times for being an AI architect. The field is in its infancy and there are many excellent and challenging domains where one can make a significant impact. In this new era one needs to accept that there will be constant learning and adaptation. As the techniques and concepts of AI/ML continue to increase, the combinations and applications simply keep growing. That said, having a solid foundational knowledge of AI/ML concepts shall mitigate short-term knowledge gaps. Are you ready to become an AI architect?</p>]]></content:encoded></item><item><title><![CDATA[Thinking Computationally]]></title><description><![CDATA[The complete Chapter 1: Thinking Computationally from The C++ Programmer's Mindset by Sam Morley, Packt 2025.]]></description><link>https://deepengineering.substack.com/p/thinking-computationally</link><guid isPermaLink="false">https://deepengineering.substack.com/p/thinking-computationally</guid><dc:creator><![CDATA[Sam Morley]]></dc:creator><pubDate>Thu, 22 Jan 2026 08:31:44 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/7667fcd3-2279-4e00-a74d-ff1459f26896_800x533.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p></p><div class="file-embed-wrapper" data-component-name="FileToDOM"><div class="file-embed-container-reader"><div class="file-embed-container-top"><image class="file-embed-thumbnail-default" src="https://substackcdn.com/image/fetch/$s_!0Cy0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack.com%2Fimg%2Fattachment_icon.svg"></image><div class="file-embed-details"><div class="file-embed-details-h1">Chapter 1: Thinking Computationally (from The C++ Programmer's Mindset By Sam Morley) &#169; Packt (All Rights Reserved)</div><div class="file-embed-details-h2">1.08MB &#8729; PDF file</div></div><a class="file-embed-button wide" href="https://deepengineering.substack.com/api/v1/file/759e4df6-4d3b-4025-8187-7f8cc1d62ccd.pdf"><span class="file-embed-button-text">Download</span></a></div><div class="file-embed-description">Solving problems is a central part of being a programmer, as well as a useful skill for everyday life. The methodology is broadly the same wherever you look: identify smaller, more tractable challenges; realize these as instances of a general class of problem; solve the intermediate challenges; and put everything together as a sequence of simple steps to solve the larger problem. In computer science, we call this computational thinking.

This chapter serves as an introduction to the basic components of computational thinking at a high level. The objectives are to lay the foundation for more detailed analysis and in-depth examples later in the book. The first part of the chapter introduces the four components of computational thinking (decomposition, abstraction, pattern recognition, and algorithm design). The second half of the chapter deals with C++ specifically and identifies some aspects of the C++ language and standard library that can not only help implement efficient solutions, but also help you think about the problems themselves.

It is important to remember that the four components of computational thinking are not a step-by-step guide to solving problems. Learning how and when these different components come together to deliver a solution relies on a good knowledge of the tools and methodologies available to you as the solver, and on your past experience. This chapter will help you get started with building the necessary foundations of the theory and set the stage for building out some basic examples to get you started with tackling larger and more complex problems later.

Solving problems is an iterative process. There will be many failed attempts and false starts. This is a necessary part of the process. The last part of the chapter deals with good software practices that will enable you to iterate quickly and easily on your designs and arrive at a correct and usable solution more quickly.

In this chapter, we&#8217;re going to cover the following main topics:
&#8226;&#9;The components of computational thinking
&#8226;&#9;Decomposing problems
&#8226;&#9;Building abstractions and recognizing common patterns
&#8226;&#9;Understanding algorithms
&#8226;&#9;Using modern C++ and good practice

Download to continue reading...</div><a class="file-embed-button narrow" href="https://deepengineering.substack.com/api/v1/file/759e4df6-4d3b-4025-8187-7f8cc1d62ccd.pdf"><span class="file-embed-button-text">Download</span></a></div></div><div><hr></div><p>To go deeper check out <em><strong><a href="https://www.packtpub.com/en-us/product/the-c-programmers-mindset-9781835888438">The C++ Programmer&#8217;s Mindset</a></strong> </em>(Sam Morley, Packt, 1st ed., Nov 2025). The book introduces computational thinking as a practical framework&#8212;decomposition, abstraction, and pattern recognition&#8212;and shows how to apply it using modern C++ features to build solutions that are maintainable, efficient, and reusable. Across small examples and a larger case study, Morley covers using algorithms and data structures effectively, designing modular code, analyzing performance, and scaling work with concurrency, GPUs, and profiling tools&#8212;aimed at intermediate C++ developers who want to strengthen both their technical toolkit and the way they approach complex software challenges.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rpnO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F207a7e01-560e-4438-ad85-562040511902_2250x2775" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rpnO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F207a7e01-560e-4438-ad85-562040511902_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!rpnO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F207a7e01-560e-4438-ad85-562040511902_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!rpnO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F207a7e01-560e-4438-ad85-562040511902_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!rpnO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F207a7e01-560e-4438-ad85-562040511902_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rpnO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F207a7e01-560e-4438-ad85-562040511902_2250x2775" width="332" height="409.5274725274725" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/207a7e01-560e-4438-ad85-562040511902_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:332,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The C++ Programmer's Mindset&quot;,&quot;title&quot;:&quot;The C++ Programmer's Mindset&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The C++ Programmer's Mindset" title="The C++ Programmer's Mindset" srcset="https://substackcdn.com/image/fetch/$s_!rpnO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F207a7e01-560e-4438-ad85-562040511902_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!rpnO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F207a7e01-560e-4438-ad85-562040511902_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!rpnO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F207a7e01-560e-4438-ad85-562040511902_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!rpnO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F207a7e01-560e-4438-ad85-562040511902_2250x2775 1456w" sizes="100vw" loading="lazy" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here&#8217;s what some readers have said:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xpla!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a0e02e-b5b2-448e-aeb6-902f4cadb1df_857x607.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xpla!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a0e02e-b5b2-448e-aeb6-902f4cadb1df_857x607.png 424w, https://substackcdn.com/image/fetch/$s_!xpla!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a0e02e-b5b2-448e-aeb6-902f4cadb1df_857x607.png 848w, https://substackcdn.com/image/fetch/$s_!xpla!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a0e02e-b5b2-448e-aeb6-902f4cadb1df_857x607.png 1272w, https://substackcdn.com/image/fetch/$s_!xpla!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a0e02e-b5b2-448e-aeb6-902f4cadb1df_857x607.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xpla!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a0e02e-b5b2-448e-aeb6-902f4cadb1df_857x607.png" width="857" height="607" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/95a0e02e-b5b2-448e-aeb6-902f4cadb1df_857x607.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:607,&quot;width&quot;:857,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:138673,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/185273208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a0e02e-b5b2-448e-aeb6-902f4cadb1df_857x607.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!xpla!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a0e02e-b5b2-448e-aeb6-902f4cadb1df_857x607.png 424w, https://substackcdn.com/image/fetch/$s_!xpla!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a0e02e-b5b2-448e-aeb6-902f4cadb1df_857x607.png 848w, https://substackcdn.com/image/fetch/$s_!xpla!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a0e02e-b5b2-448e-aeb6-902f4cadb1df_857x607.png 1272w, https://substackcdn.com/image/fetch/$s_!xpla!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a0e02e-b5b2-448e-aeb6-902f4cadb1df_857x607.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!doxG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac0d1272-d603-4e1c-8aec-7407048c6dd9_852x422.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!doxG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac0d1272-d603-4e1c-8aec-7407048c6dd9_852x422.png 424w, https://substackcdn.com/image/fetch/$s_!doxG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac0d1272-d603-4e1c-8aec-7407048c6dd9_852x422.png 848w, https://substackcdn.com/image/fetch/$s_!doxG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac0d1272-d603-4e1c-8aec-7407048c6dd9_852x422.png 1272w, https://substackcdn.com/image/fetch/$s_!doxG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac0d1272-d603-4e1c-8aec-7407048c6dd9_852x422.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!doxG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac0d1272-d603-4e1c-8aec-7407048c6dd9_852x422.png" width="852" height="422" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ac0d1272-d603-4e1c-8aec-7407048c6dd9_852x422.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:422,&quot;width&quot;:852,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:100846,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/185273208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac0d1272-d603-4e1c-8aec-7407048c6dd9_852x422.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!doxG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac0d1272-d603-4e1c-8aec-7407048c6dd9_852x422.png 424w, https://substackcdn.com/image/fetch/$s_!doxG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac0d1272-d603-4e1c-8aec-7407048c6dd9_852x422.png 848w, https://substackcdn.com/image/fetch/$s_!doxG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac0d1272-d603-4e1c-8aec-7407048c6dd9_852x422.png 1272w, https://substackcdn.com/image/fetch/$s_!doxG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac0d1272-d603-4e1c-8aec-7407048c6dd9_852x422.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ATit!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8182e4a3-ef29-458d-9515-4bad5724839a_862x662.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ATit!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8182e4a3-ef29-458d-9515-4bad5724839a_862x662.png 424w, https://substackcdn.com/image/fetch/$s_!ATit!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8182e4a3-ef29-458d-9515-4bad5724839a_862x662.png 848w, https://substackcdn.com/image/fetch/$s_!ATit!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8182e4a3-ef29-458d-9515-4bad5724839a_862x662.png 1272w, https://substackcdn.com/image/fetch/$s_!ATit!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8182e4a3-ef29-458d-9515-4bad5724839a_862x662.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ATit!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8182e4a3-ef29-458d-9515-4bad5724839a_862x662.png" width="862" height="662" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8182e4a3-ef29-458d-9515-4bad5724839a_862x662.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:662,&quot;width&quot;:862,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:134677,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/185273208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8182e4a3-ef29-458d-9515-4bad5724839a_862x662.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!ATit!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8182e4a3-ef29-458d-9515-4bad5724839a_862x662.png 424w, https://substackcdn.com/image/fetch/$s_!ATit!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8182e4a3-ef29-458d-9515-4bad5724839a_862x662.png 848w, https://substackcdn.com/image/fetch/$s_!ATit!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8182e4a3-ef29-458d-9515-4bad5724839a_862x662.png 1272w, https://substackcdn.com/image/fetch/$s_!ATit!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8182e4a3-ef29-458d-9515-4bad5724839a_862x662.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Fundamentals of AI System Architecture]]></title><description><![CDATA[The complete "Chapter 1: Fundamentals of AI System Architecture" from the book, Architecting AI Software Systems by Richard D Avila and Imran Ahmad]]></description><link>https://deepengineering.substack.com/p/fundamentals-of-ai-system-architecture</link><guid isPermaLink="false">https://deepengineering.substack.com/p/fundamentals-of-ai-system-architecture</guid><dc:creator><![CDATA[Imran Ahmad]]></dc:creator><pubDate>Thu, 04 Dec 2025 05:23:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!mCUY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe338efee-2dce-4923-b2a9-dc7261e4317a_761x541.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The recent surge of public interest in <strong>Artificial Intelligence</strong> (<strong>AI</strong>), particularly with the rise of generative AI, has ignited a wave of excitement and demand for comprehensive AI solutions. This heightened interest extends beyond tech enthusiasts and researchers to businesses, governments, and individuals seeking to harness AI&#8217;s power to solve real-world problems and enhance their capabilities. In this landscape, the architecture of AI systems, which defines their structure, components, and interactions, plays a pivotal role in shaping the development and deployment of effective AI solutions.</p><p>AI has emerged as a transformative force, revolutionizing industries and reshaping the way we interact with technology and the world around us. At its core, AI refers to computational models that mimic human cognitive functions, including learning from data, recognizing patterns, making decisions, and even interacting with their environment. This revolutionary technology spans a wide spectrum, from simple rule-based systems to sophisticated deep learning models, each with unique applications and capabilities.</p><p>A major aspect of any AI system is that the results of the inference being done need to be relevant and trusted. To ensure trust is gained and maintained, the use of strong architecture is paramount. One not only architects the technology but also how the technology is going to be used, managed, and evaluated by the span of stakeholders. The stakeholders need to be able to pinpoint issues, rapidly correct model parameters, and deploy changes in a deliberate and rapid manner. In more common parlance, the architecting and supporting processes can be described as &#8220;guard rails.&#8221; How one employs guard rails is very specific to the domain and use case that is to use the AI technology. There are classes of guard rails that can be discussed &#8211; for example, the use of canaries to judge model correctness from a known gold standard, time and data flow metrics to judge model performance, and the use of filters and robust data quality checks so that only consistent and correct data enters the system. Another class of guardrails is human system interfaces, such as alerting frameworks to classify errors and monitors, the use of troubleshooting tools, and preset protocols for handling unexpected errors. Written procedures or guidance from modeling allow for the maintenance of a system without the need to call upon the model developer to do troubleshooting.</p><p>Trust is a paramount consideration for system success, so one needs to architect a system with that in mind. In many ways, the presentation and lessons learned described in this book look to ensure trust in an AI system.</p><p>This chapter highlights, in a broad sense, the key aspects of AI architecture considerations that drive a successful AI implementation. The topics are as follows:</p><ul><li><p>Introduction and key AI concepts</p></li><li><p>Components of an AI system</p></li><li><p>AI technologies and microservices</p></li><li><p>AI systems and technical considerations</p></li><li><p>Deployment considerations</p></li></ul><div><hr></div><h1>Introduction to AI systems: architecting the future of intelligence</h1><p>AI systems are the embodiment of AI, acting as the engines that power intelligent applications and services. These systems are intricate constructs, meticulously designed to perform a diverse range of tasks, from image recognition and natural language processing to autonomous decision-making and complex problem-solving.</p><p>The architecture of an AI system functions as a detailed technical blueprint, specifying its structural organization and the precise interactions between its various components. These components include the following:</p><ul><li><p><strong>Hardware infrastructure</strong>: CPUs for general processing, GPUs for parallel computation, TPUs for tensor operations, and specialized AI accelerators</p></li><li><p><strong>Software frameworks</strong>: TensorFlow, PyTorch, JAX, and other libraries that enable model development</p></li><li><p><strong>Algorithmic implementations</strong>: Machine learning algorithms, neural network architectures, and inference engines</p></li><li><p><strong>Data pipelines</strong>: ETL processes, feature stores, and data management systems</p></li></ul><p>All these elements work in a coordinated operation to enable the system to fulfill its designed objectives efficiently and reliably.</p><p>A well-architected AI system achieves several critical technical requirements:</p><ul><li><p><strong>Optimal performance</strong>: Maximizes computational efficiency to deliver responsive and accurate results with minimal latency. This involves an optimized model design, efficient resource allocation, and hardware-aware implementations that fully utilize available computing capabilities.</p></li><li><p><strong>Scalability</strong>: Handles growing workloads and expanding datasets through both horizontal scaling (adding more machines) and vertical scaling (adding more powerful machines) without performance degradation. Modern AI architectures must accommodate increasing data volumes, user bases, and computational demands.</p></li><li><p><strong>Efficiency</strong>: Reduces computational resource consumption, energy usage, and operational costs through techniques such as model quantization, knowledge distillation, and optimized inference paths. Efficient AI systems minimize their resource footprint while maintaining functional effectiveness.</p></li><li><p><strong>Reliability</strong>: Ensures consistent operation with high-availability metrics, even when facing unexpected data patterns, input variations, or system failures. This requires robust error handling, graceful degradation capabilities, and comprehensive monitoring systems. Given that AI technologies can be both deterministic and non-deterministic, consideration must be given to allow for human intervention. This intervention needs to span the gamut from simple monitoring to a full suite of testing infrastructure.</p></li><li><p><strong>Security</strong>: Implements comprehensive data protection measures and defends against adversarial attacks, data poisoning, and model vulnerabilities. AI systems must maintain data confidentiality and integrity, and be resilient against both traditional cybersecurity threats and AI-specific attacks.</p></li><li><p><strong>Explainability</strong>: Provides transparent visibility into algorithmic decision processes, supporting regulatory compliance, user trust, and system debugging. Modern AI architectures must balance performance with interpretability to meet growing demands for AI transparency.</p></li></ul><p>The field of AI is constantly evolving, with new architectures and technologies emerging at a rapid pace. As we delve deeper into this fascinating domain, we will explore the various types of AI systems, their underlying principles, and the diverse applications that are shaping the future of technology and society.</p><h2>What is an AI system?</h2><p>An AI system is a computational model or a collection of models designed to perform tasks that typically require human intelligence. These systems are powered by algorithms and data, enabling them to learn from experience, adapt to new information, and make decisions or predictions.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!u0lW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F096b15fa-95f0-474e-a1e6-6973015e02e0_208x769.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u0lW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F096b15fa-95f0-474e-a1e6-6973015e02e0_208x769.png 424w, https://substackcdn.com/image/fetch/$s_!u0lW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F096b15fa-95f0-474e-a1e6-6973015e02e0_208x769.png 848w, https://substackcdn.com/image/fetch/$s_!u0lW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F096b15fa-95f0-474e-a1e6-6973015e02e0_208x769.png 1272w, https://substackcdn.com/image/fetch/$s_!u0lW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F096b15fa-95f0-474e-a1e6-6973015e02e0_208x769.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!u0lW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F096b15fa-95f0-474e-a1e6-6973015e02e0_208x769.png" width="208" height="769" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/096b15fa-95f0-474e-a1e6-6973015e02e0_208x769.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:769,&quot;width&quot;:208,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!u0lW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F096b15fa-95f0-474e-a1e6-6973015e02e0_208x769.png 424w, https://substackcdn.com/image/fetch/$s_!u0lW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F096b15fa-95f0-474e-a1e6-6973015e02e0_208x769.png 848w, https://substackcdn.com/image/fetch/$s_!u0lW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F096b15fa-95f0-474e-a1e6-6973015e02e0_208x769.png 1272w, https://substackcdn.com/image/fetch/$s_!u0lW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F096b15fa-95f0-474e-a1e6-6973015e02e0_208x769.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 1.4: AI technology stack</figcaption></figure></div><p>From an implementation perspective, AI systems typically consist of several key layers:</p><ol><li><p><strong>Hardware</strong>: Encompasses compute resources such as CPU, GPU, TPUs, full-spectrum storage, and networking</p></li><li><p><strong>Data layer</strong>: Handles data ingestion, storage, preprocessing, and feature engineering</p></li><li><p><strong>Model layer</strong>: Contains the trained machine learning or deep learning models</p></li><li><p><strong>Inference layer</strong>: Manages the execution of models against new data inputs</p></li><li><p><strong>Application layer</strong>: Integrates AI capabilities into user-facing applications</p></li><li><p><strong>Monitoring layer</strong>: Tracks system performance, data drift, and model health</p></li></ol><p>AI systems can be classified into two broad categories:</p><ul><li><p><strong>Narrow AI (weak AI)</strong>: These systems are designed to excel at specific tasks within a limited domain. Examples include image recognition software, spam filters, and recommendation engines. While they may be highly proficient at their designated tasks, they lack the ability to generalize their knowledge in other areas.</p></li><li><p><strong>General AI (strong AI)</strong>: This is a theoretical concept of an AI system that possesses human-level intelligence and can perform any intellectual task that a human can. It would have the ability to reason, plan, solve problems, learn from experience, and understand complex ideas across diverse domains. While general AI remains a distant goal, significant progress has been made in developing systems with increasingly sophisticated capabilities.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mCUY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe338efee-2dce-4923-b2a9-dc7261e4317a_761x541.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mCUY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe338efee-2dce-4923-b2a9-dc7261e4317a_761x541.png 424w, https://substackcdn.com/image/fetch/$s_!mCUY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe338efee-2dce-4923-b2a9-dc7261e4317a_761x541.png 848w, https://substackcdn.com/image/fetch/$s_!mCUY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe338efee-2dce-4923-b2a9-dc7261e4317a_761x541.png 1272w, https://substackcdn.com/image/fetch/$s_!mCUY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe338efee-2dce-4923-b2a9-dc7261e4317a_761x541.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mCUY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe338efee-2dce-4923-b2a9-dc7261e4317a_761x541.png" width="761" height="541" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e338efee-2dce-4923-b2a9-dc7261e4317a_761x541.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:541,&quot;width&quot;:761,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A screenshot of a computer\n\nAI-generated content may be incorrect.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A screenshot of a computer

AI-generated content may be incorrect." title="A screenshot of a computer

AI-generated content may be incorrect." srcset="https://substackcdn.com/image/fetch/$s_!mCUY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe338efee-2dce-4923-b2a9-dc7261e4317a_761x541.png 424w, https://substackcdn.com/image/fetch/$s_!mCUY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe338efee-2dce-4923-b2a9-dc7261e4317a_761x541.png 848w, https://substackcdn.com/image/fetch/$s_!mCUY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe338efee-2dce-4923-b2a9-dc7261e4317a_761x541.png 1272w, https://substackcdn.com/image/fetch/$s_!mCUY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe338efee-2dce-4923-b2a9-dc7261e4317a_761x541.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 1.5: Classification of AI systems</figcaption></figure></div><p>The pervasive impact of AI infrastructure: powering intelligent solutions across industries</p><p>Well-architected AI infrastructure, encompassing the hardware, software, and networks that support AI applications, is the driving force behind the transformative impact of AI across industries. This infrastructure enables the deployment and scaling of AI models, algorithms, and frameworks, unlocking their full potential to address complex challenges and deliver innovative solutions.</p><ul><li><p><strong>Healthcare:</strong></p><ul><li><p><strong>Accelerated medical image analysis</strong>: High-performance computing clusters and specialized hardware accelerators enable rapid processing of medical images, facilitating faster and more accurate diagnosis.</p></li><li><p><strong>Data-driven insights</strong>: Scalable storage and processing infrastructure empowers AI-driven analytics on vast patient datasets, leading to personalized treatment plans and improved patient outcomes.</p></li><li><p><strong>Real-time monitoring</strong>: Cloud-based AI infrastructure enables continuous monitoring of patient vitals and other health data, facilitating timely interventions and proactive care.</p></li></ul></li><li><p><strong>Finance:</strong></p><ul><li><p><strong>Robust fraud detection:</strong> Distributed computing and real-time analytics platforms empower AI models to detect fraudulent transactions with greater accuracy and speed, protecting financial institutions and consumers.</p></li><li><p><strong>Optimized trading strategies</strong>: High-frequency trading algorithms leverage low-latency networks and powerful computational resources to execute trades with precision and efficiency, maximizing returns.</p></li><li><p><strong>Personalized financial services:</strong> Cloud-based AI infrastructure enables the deployment of robo-advisors and other AI-powered tools that provide tailored financial advice and services to individuals.</p></li></ul></li><li><p><strong>Autonomous vehicles:</strong></p><ul><li><p><strong>Real-time sensor fusion:</strong> High-throughput data pipelines and edge computing infrastructure enable the rapid processing of sensor data from cameras, lidar, radar, and other sources, ensuring timely decision-making for autonomous vehicles.</p></li><li><p><strong>Enhanced object recognition:</strong> Deep learning models trained on massive datasets and deployed on specialized hardware accelerators enable accurate and reliable identification of objects in the environment.</p></li><li><p><strong>Optimized navigation:</strong> Cloud-based mapping and navigation services, combined with onboard AI processing, provide autonomous vehicles with real-time information and guidance for safe and efficient navigation.</p></li></ul></li></ul><p>The continued development and optimization of AI infrastructure will play a crucial role in realizing the full potential of AI across industries. By providing the foundation for performant and scalable AI solutions, this infrastructure is poised to transform the way we live and work.</p><h2>Key components of AI system architectures</h2><p>AI systems, in their essence, are complex structures designed to emulate human cognitive abilities such as learning, reasoning, and problem-solving. To achieve these capabilities, AI systems rely on a well-defined architecture comprising several interconnected components, each playing a crucial role in the overall functioning of the system. Understanding these key components is fundamental to comprehending the inner workings and potential of AI.</p><ul><li><p><strong>Data components</strong>: Data serves as the lifeblood of any AI system, acting as the raw material upon which the system learns and improves. Data can exist in multiple forms:</p><ul><li><p><strong>Structured data</strong>: Organized in predefined formats such as databases and spreadsheets</p></li><li><p><strong>Semi-structured data</strong>: Partially organized information such as JSON or XML files</p></li><li><p><strong>Unstructured data</strong>: Raw information, including text documents, images, audio recordings, and video files</p></li></ul></li></ul><p>The quality, quantity, and relevance of the data significantly impact the AI system&#8217;s performance and ability to generalize to new situations.</p><ul><li><p><strong>Algorithmic frameworks:</strong> Algorithms are engines driving AI systems, providing instructions and logic for processing data and generating intelligent outputs. Machine learning algorithms, a subset of AI algorithms, empower systems to learn patterns and relationships from data, enabling them to make predictions, classifications, or decisions. Common algorithmic approaches in production AI systems include the following:</p><ul><li><p><strong>Traditional machine learning</strong>: Linear regression, random forests, gradient boosting, and support vector machines</p></li><li><p><strong>Deep learning</strong>: <strong>Convolutional neural networks</strong> (<strong>CNN</strong>s), <strong>Recurrent Neural Networks</strong> (<strong>RNN</strong>s), transformers, and graph neural networks</p></li><li><p><strong>Reinforcement learning</strong>: Q-learning, policy gradient methods, and actor-critic architectures</p></li></ul></li></ul><p>The selection of appropriate algorithms depends on the specific problem domain, available data characteristics, and performance requirements.</p><ul><li><p><strong>Model architectures:</strong> Models represent the culmination of the learning process in AI systems. They are mathematical representations of the knowledge extracted from data, encapsulating the patterns, relationships, and insights discovered by the algorithms. These models can be simple or complex, depending on the nature of the task and the algorithm used. Model architectures range between the following:</p><ul><li><p><strong>Simple linear models</strong>: Easily interpretable but limited in capability</p></li><li><p><strong>Ensemble models</strong>: Combining multiple simpler models for improved performance</p></li><li><p><strong>Deep neural networks</strong>: Complex architectures with millions or billions of parameters</p></li></ul></li></ul><p>Once trained, models are used to make predictions or decisions on new, unseen data.</p><ul><li><p><strong>Infrastructure:</strong> The infrastructure component encompasses the hardware and software resources that provide the computational power and environment necessary for AI systems to operate. Key infrastructure elements include the following:</p><ul><li><p><strong>Computational resources</strong>: High-performance servers, specialized AI accelerators (GPUs, TPUs, FPGAs), and distributed computing clusters</p></li><li><p><strong>Storage systems</strong>: High-throughput, scalable storage for training data and model artifacts</p></li><li><p><strong>Networking components</strong>: Low-latency interconnects for distributed training and inference</p></li></ul></li><li><p><strong>Development frameworks</strong>: Software libraries such as TensorFlow, PyTorch, and Hugging Face that streamline AI development and deployment.</p></li></ul><p>Understanding these key components and their interactions provides a solid foundation for comprehending the complex landscape of AI system architectures. By carefully designing and optimizing each component, researchers and engineers can build AI systems that are capable of tackling a wide range of tasks and applications, from image recognition and natural language processing to autonomous driving and drug discovery. The integration of AI capabilities into existing software stacks requires thoughtful architectural considerations to successfully incorporate intelligence while addressing the unique requirements that AI components introduce. These specific requirements and architectural approaches form the central focus of this book. Due to the complexity of AI systems, the nature of the deployment approach is paramount. The next section will discuss the use of microservice architectures that provide a balance between performance and modularity.</p><div><hr></div><h1>Microservice architectures: a modular approach to building complex AI systems</h1><p>As <strong>AI</strong> systems grow in complexity, traditional monolithic architectures can become unwieldy, hindering development speed and flexibility. Microservice architectures offer a compelling alternative by breaking down these complex systems into smaller, independent services. Each microservice focuses on a specific function and communicates with others through well-defined APIs.</p><h2>Advantages of microservices for AI</h2><ul><li><p><strong>Enhanced agility and flexibility</strong>: Teams can independently develop, deploy, and update each microservice, using the most suitable technologies and programming languages for each task. This accelerates development cycles and allows for easier experimentation and innovation.</p></li><li><p><strong>Improved scalability</strong>: Microservices can be scaled horizontally to meet specific demand, ensuring optimal resource utilization. For example, a service handling image processing can be scaled independently of a service responsible for natural language understanding.</p></li><li><p><strong>Increased resilience and fault isolation</strong>: If a microservice fails, the impact is localized, minimizing disruption to the entire system. This enhances overall reliability and simplifies troubleshooting.</p></li><li><p><strong>Technological diversity</strong>: Microservice architectures empower teams to leverage the best tools for each task, promoting innovation and allowing for gradual technology upgrades.</p></li></ul><h2>Challenges of microservice architectures</h2><ul><li><p><strong>Increased complexity</strong>: Managing a multitude of services and their interactions requires robust orchestration and monitoring tools. Service discovery, load balancing, and failure handling become critical considerations.</p></li><li><p><strong>Communication overhead</strong>: Excessive inter-service communication can introduce latency and impact overall performance. The careful design of APIs and communication patterns is essential to mitigate this issue.</p></li><li><p><strong>Data consistency</strong>: Maintaining data consistency across distributed services can be challenging. Strategies such as eventual consistency or distributed transactions may be required to ensure data integrity.</p></li></ul><h2>Real-world example: conversational AI microservices implementation</h2><p>To illustrate how a microservices approach can streamline a conversational AI solution, let us examine a practical example that demonstrates how these principles come to life. This section explores a conversational AI system &#8211; such as a chatbot or virtual assistant &#8211; built using a four-service microservices architecture with an API gateway.</p><h3>The four core microservices</h3><p><em>Figure 1.6</em> illustrates the high-level design of our conversational AI system:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sWP_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e45309e-9868-480f-ba2a-b1b58ef387d2_823x579.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sWP_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e45309e-9868-480f-ba2a-b1b58ef387d2_823x579.png 424w, https://substackcdn.com/image/fetch/$s_!sWP_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e45309e-9868-480f-ba2a-b1b58ef387d2_823x579.png 848w, https://substackcdn.com/image/fetch/$s_!sWP_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e45309e-9868-480f-ba2a-b1b58ef387d2_823x579.png 1272w, https://substackcdn.com/image/fetch/$s_!sWP_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e45309e-9868-480f-ba2a-b1b58ef387d2_823x579.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sWP_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e45309e-9868-480f-ba2a-b1b58ef387d2_823x579.png" width="823" height="579" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3e45309e-9868-480f-ba2a-b1b58ef387d2_823x579.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:579,&quot;width&quot;:823,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A diagram of a service\n\nAI-generated content may be incorrect.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A diagram of a service

AI-generated content may be incorrect." title="A diagram of a service

AI-generated content may be incorrect." srcset="https://substackcdn.com/image/fetch/$s_!sWP_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e45309e-9868-480f-ba2a-b1b58ef387d2_823x579.png 424w, https://substackcdn.com/image/fetch/$s_!sWP_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e45309e-9868-480f-ba2a-b1b58ef387d2_823x579.png 848w, https://substackcdn.com/image/fetch/$s_!sWP_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e45309e-9868-480f-ba2a-b1b58ef387d2_823x579.png 1272w, https://substackcdn.com/image/fetch/$s_!sWP_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e45309e-9868-480f-ba2a-b1b58ef387d2_823x579.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 1.6: Conversational AI microservices</figcaption></figure></div><p>The architecture consists of four core specialized services plus an API gateway:</p><ol><li><p>Language understanding service:</p><ul><li><p><strong>Primary functions</strong>: Intent classification, entity identification/extraction, and hosting of NLP models.</p></li><li><p><strong>Data and models</strong>: References one or more NLP model databases (for example, transformer-based classifiers).</p></li><li><p><strong>Key interactions</strong>: Receives the user&#8217;s text (through the API gateway), determines the user&#8217;s intent (e.g., &#8220;Check account balance&#8221;), and extracts relevant entities (e.g., &#8220;date,&#8221; &#8220;location,&#8221; &#8220;product name&#8221;).</p></li></ul></li><li><p>Dialog management service:</p><ul><li><p><strong>Primary functions</strong>: Oversees conversation flow, handles session state, and orchestrates the next step in the dialog.</p></li><li><p><strong>Data and state</strong>: Maintains conversation context in a dedicated state database.</p></li><li><p><strong>Key interactions</strong>: Logs conversation events (asynchronously) and updates or retrieves session details to guide the flow (e.g., &#8220;Greeting,&#8221; &#8220;Confirmation,&#8221; &#8220;Next step&#8221;).</p></li></ul></li><li><p>Knowledge response service</p><ul><li><p><strong>Primary functions</strong>: Retrieves relevant information and formulates responses. This might involve querying a knowledge base (e.g., FAQs, product info) or assembling template-based replies.</p></li><li><p><strong>Data and templates</strong>: Stores domain-specific data in a knowledge DB and uses templates or generative mechanisms for response creation.</p></li><li><p><strong>Key interactions</strong>: Receives queries from the dialog management service, finds or composes the best response, and returns it for final delivery to the user.</p></li></ul></li><li><p>Conversation analytics service:</p><ul><li><p><strong>Primary functions</strong>: Processes logs and usage metrics for reporting, visualization, and deeper analytics (e.g., intent distribution, user satisfaction trends).</p></li><li><p><strong>Data and reporting</strong>: Maintains analytics data in a separate database for dashboards or offline processing.</p></li><li><p><strong>Key interactions</strong>: Collects asynchronous event logs from the dialog management service and other components to measure performance, track user behavior, and provide insights that could improve the system over time.</p></li></ul></li></ol><h3>Role of the API gateway</h3><p>Although not counted as one of the four microservices, the <strong>API gateway</strong> is a vital component at the front of the architecture. It does the following:</p><ul><li><p>Receives requests from the user (via text or other channels)</p></li><li><p>Initializes the session and routes incoming data to the language understanding service</p></li><li><p>Forwards recognized intents and updates to the dialog management service</p></li><li><p>Passes replies from downstream services back to the user</p></li></ul><p>By centralizing traffic management, the API gateway enforces consistent security, throttling, and monitoring policies while keeping each microservice isolated and independently scalable.</p><h3>Conversation flow sequence</h3><p>To illustrate how these microservices interact during a typical user journey, <em>Figure 1.7</em> shows the sequence of calls between them in a single conversation cycle:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NItB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c9c6e30-c2c7-4f66-a3b7-c02fc2488837_793x808.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NItB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c9c6e30-c2c7-4f66-a3b7-c02fc2488837_793x808.png 424w, https://substackcdn.com/image/fetch/$s_!NItB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c9c6e30-c2c7-4f66-a3b7-c02fc2488837_793x808.png 848w, https://substackcdn.com/image/fetch/$s_!NItB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c9c6e30-c2c7-4f66-a3b7-c02fc2488837_793x808.png 1272w, https://substackcdn.com/image/fetch/$s_!NItB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c9c6e30-c2c7-4f66-a3b7-c02fc2488837_793x808.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NItB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c9c6e30-c2c7-4f66-a3b7-c02fc2488837_793x808.png" width="793" height="808" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4c9c6e30-c2c7-4f66-a3b7-c02fc2488837_793x808.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:808,&quot;width&quot;:793,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A diagram of a software project\n\nAI-generated content may be incorrect.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A diagram of a software project

AI-generated content may be incorrect." title="A diagram of a software project

AI-generated content may be incorrect." srcset="https://substackcdn.com/image/fetch/$s_!NItB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c9c6e30-c2c7-4f66-a3b7-c02fc2488837_793x808.png 424w, https://substackcdn.com/image/fetch/$s_!NItB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c9c6e30-c2c7-4f66-a3b7-c02fc2488837_793x808.png 848w, https://substackcdn.com/image/fetch/$s_!NItB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c9c6e30-c2c7-4f66-a3b7-c02fc2488837_793x808.png 1272w, https://substackcdn.com/image/fetch/$s_!NItB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c9c6e30-c2c7-4f66-a3b7-c02fc2488837_793x808.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 1.7: Sequence diagram between system components</figcaption></figure></div><p>The sequence progresses as follows:</p><ol><li><p><strong>User &#8594; API gateway</strong>: The user sends a request (e.g., a chat message). The API gateway initializes the session (if needed) and forwards the message to the language understanding service.</p></li><li><p><strong>Language understanding service</strong>:</p><ul><li><p>Performs intent classification and entity identification.</p></li><li><p>Returns a recognized intent (e.g., &#8220;CheckWeather&#8221;) and any extracted entities (e.g., date, location) to the API gateway.</p></li></ul></li><li><p><strong>Dialog management service</strong>:</p><ul><li><p>Receives recognized intent from the API gateway.</p></li><li><p>Logs conversation events (asynchronously) into the conversation analytics service.</p></li><li><p>Updates or retrieves the <strong>session state</strong> (e.g., user&#8217;s location or recent conversation context).</p></li></ul></li><li><p><strong>Knowledge response service</strong>:</p><ul><li><p>Once the dialog management service determines additional data is needed (e.g., weather info, product detail), it sends a <strong>query</strong> to the knowledge response service.</p></li><li><p>This service fetches the necessary information or constructs a response template (e.g., &#8220;The weather for your location is sunny with 75&#176;F...&#8221;).</p></li></ul></li><li><p><strong>Conversation analytics service (asynchronous logging)</strong>:</p><ul><li><p>Continuously receives usage data and conversation logs from the dialog management service (and possibly from the knowledge response service).</p></li><li><p>Processes and stores these logs for future reporting (e.g., monthly usage dashboards, model performance metrics).</p></li></ul></li><li><p><strong>Reply to the user</strong>:</p><ul><li><p>The knowledge response service&#8217;s formulated answer is routed back through the dialog management service (if necessary, for final session updates) and then returned via the API gateway.</p></li><li><p>The user receives the <strong>reply</strong> and the interaction concludes.</p></li></ul></li></ol><h3>Key aspects of microservice communication</h3><ul><li><p>Synchronous versus asynchronous calls:</p><ul><li><p>Requests that must return immediately (e.g., generating a response for the user) use synchronous calls.</p></li><li><p>Logging or analytics operations are typically performed asynchronously to avoid slowing down the core conversation loop.</p></li></ul></li><li><p>Stateful versus stateless components:</p><ul><li><p>Dialog management requires tracking session state, while other services (e.g., language understanding) often benefit from stateless designs for simpler scaling.</p></li><li><p>The dialog management service may require robust state management solutions, such as distributed caches or databases.</p></li></ul></li><li><p>Service autonomy:</p><ul><li><p>Each microservice can be updated or replaced independently without affecting the rest of the system.</p></li><li><p>The language understanding service&#8217;s NLP models may need frequent retraining. Because it is a separate service, such updates can be deployed without disrupting the other services.</p></li></ul></li><li><p>Data isolation:</p><ul><li><p>Services manage their own domain data. Dialog management stores conversation state, knowledge response holds domain facts, and analytics maintains interaction logs.</p></li><li><p>Sensitive user data should be restricted to the dialog management service&#8217;s state store when necessary, minimizing the exposure across the entire system.</p></li></ul></li></ul><h3>Implementation considerations for conversational AI microservices</h3><ol><li><p>Scaling independently:</p><ul><li><p>The language understanding service can be scaled up or down based on incoming message load (e.g., horizontal autoscaling for peak chat traffic).</p></li><li><p>The dialog management service maintains conversation state and may require different scaling strategies.</p></li><li><p>The knowledge response service often scales according to the complexity of information retrieval.</p></li><li><p>The conversation analytics service can be scaled separately, especially if analytics workloads (such as report generation) spike at different times than user requests.</p></li></ul></li><li><p>Latency management:</p><ul><li><p>Conversational AI systems aim for near real-time interactions. Minimizing network hops and communication overhead between services is crucial. Using lightweight communication protocols helps ensure the system performs well at scale.</p></li></ul></li><li><p>Fault isolation:</p><ul><li><p>If one service fails (for instance, the knowledge response service goes offline), the rest of the system can still handle other tasks or offer fallback behaviors (e.g., an apology response or a redirect to a human agent).</p></li></ul></li><li><p>Monitoring and observability:</p><ul><li><p>Robust logging and observability practices are crucial to ensure the system remains resilient to service failures or slowdowns. The conversation analytics service plays a key role in tracking system health and performance.</p></li></ul></li></ol><h3>Why microservices for conversational AI?</h3><p>Breaking down a conversational AI system into these four specialized services confers significant benefits in <strong>maintainability</strong>, <strong>scalability</strong>, and <strong>team agility</strong>. Each service can evolve independently, allowing rapid iteration on NLP models, conversation flows, and knowledge retrieval strategies without risking a &#8220;big bang&#8221; failure across the entire application.</p><p>At the same time, careful attention to <strong>inter-service communication</strong> is crucial. As the sequence diagram shows, multiple hops occur for every user request. Using lightweight communication protocols and distinguishing between synchronous and asynchronous operations helps maintain system responsiveness.</p><p>The example of conversational AI powerfully illustrates how the microservices approach enables balancing flexibility, fault tolerance, and iterative innovation. The lessons learned here &#8211; such as independently scaling critical services, isolating data for security, and ensuring graceful failure modes &#8211; apply broadly to a wide array of AI-driven solutions.</p><p>This real-world implementation pattern demonstrates that while microservices add complexity, the benefits they bring to AI systems &#8211; particularly those requiring frequent updates, variable scaling, and component-level innovation &#8211; often outweigh the challenges when properly architected and implemented.</p><div><hr></div><h1>Considerations for an AI system</h1><p>Creating a well-designed AI system architecture necessitates careful consideration of several key factors. These factors ensure that the system not only functions effectively but also adapts to future demands and challenges.</p><h2>Scalability: handling growing data and model complexity</h2><p>AI systems often encounter growing volumes of data and increasingly complex models. Scalability is the ability of a system to handle this growth without compromising performance. Effective strategies include the following:</p><ul><li><p><strong>Horizontal scaling</strong>: This involves adding more compute resources to distribute the workload. For instance, in a cloud environment, you might deploy additional virtual machines or containers to handle increased traffic. Kubernetes can orchestrate these containers, ensuring that the workload is evenly distributed.</p></li><li><p><strong>Vertical scaling</strong>: Enhancing existing resources with more powerful hardware. For example, upgrading a server&#8217;s CPU or GPUs, adding more RAM, or using SSDs instead of HDDs to improve I/O performance.</p></li><li><p><strong>Distributed computing</strong>: Utilizing frameworks such as Apache Spark or Hadoop to process data across multiple nodes. This approach breaks down large datasets into smaller chunks that can be processed in parallel, significantly reducing processing time. For instance, Spark&#8217;s <strong>Resilient Distributed Datasets</strong> (<strong>RDD</strong>s) allow for in-memory processing, which is much faster than traditional disk-based processing.</p></li></ul><h2>Performance: optimization techniques</h2><p>In many AI applications, real-time or near-real-time processing is crucial. Techniques to optimize performance include the following:</p><ul><li><p><strong>Hardware acceleration</strong>: Leveraging GPUs or TPUs for computationally intensive tasks &#8211; for example, TensorFlow and PyTorch can utilize CUDA cores in NVIDIA GPUs to accelerate deep learning model training.</p></li><li><p><strong>Parallel processing</strong>: Dividing tasks into smaller sub-tasks that can be executed concurrently. In Python, libraries such as multiprocessing or concurrent.futures can be used to parallelize tasks &#8211; for instance, training multiple models simultaneously or processing different data batches in parallel.</p></li><li><p><strong>Algorithm optimization</strong>: Choosing or designing algorithms with lower computational complexity. For example, using approximate nearest neighbor algorithms for large-scale similarity search instead of exact methods, which are computationally expensive.</p></li></ul><h2>Reliability: fault tolerance, error handling, and redundancy</h2><p>Reliability is paramount, especially in critical applications. To ensure system uptime and data integrity, strategies such as fault tolerance, error handling, and redundancy are employed:</p><ul><li><p><strong>Fault tolerance</strong>: The system can continue operating even if some components fail. For example, in a microservices architecture, if one service fails, others can continue to function. Tools such as Netflix&#8217;s Hystrix can be used to implement circuit breakers to manage failures.</p></li><li><p><strong>Error handling</strong>: Mechanisms are in place to detect and correct errors gracefully &#8211; for instance, using <code>try-catch</code> blocks in code to handle exceptions and logging errors for further analysis.</p></li><li><p><strong>Redundancy</strong>: Critical components are duplicated to prevent single points of failure &#8211; for example, using RAID configurations for disk storage or deploying services in multiple availability zones in cloud environments to ensure high availability.</p></li></ul><h2>Security: data privacy and model robustness</h2><p>AI systems often handle sensitive data, making security a top priority. Key considerations include the following:</p><ul><li><p><strong>Data encryption</strong>: Protecting data at rest and in transit &#8211; for instance, using AES encryption for data stored in databases and TLS for data transmitted over networks. The use of encryption approaches needs to be considered and tested thoroughly to scope the impact on model and system performance.</p></li><li><p><strong>Access control</strong>: Implementing strict authorization and authentication mechanisms &#8211; for example, using OAuth 2.0 for secure API access and <strong>role-based access control</strong> (<strong>RBAC</strong>) to manage permissions.</p></li><li><p><strong>Model robustness</strong>: Guarding against adversarial attacks that could manipulate the system. Techniques such as adversarial training, where the model is trained on both normal and adversarial examples, can help improve robustness. Additionally, you can deploy anomaly detection systems to monitor for unusual patterns in data input.</p></li></ul><h2>Data modeling: catalogs and ontologies</h2><p>In the realm of AI, data is not just a valuable asset but the very foundation upon which intelligent systems are built. As AI models rely heavily on vast amounts of data to learn and make informed decisions, effective management and organization of this data becomes paramount. This is where data catalogs and ontologies step in as indispensable tools for navigating the complexities of data landscapes within AI architectures.</p><p>Catalogs serve as centralized repositories of metadata, providing comprehensive information about the data assets within an AI system. They act as a comprehensive index, offering insights into the data&#8217;s location, schema, lineage, quality, and other relevant attributes. By consolidating this information in a structured and accessible manner, data catalogs empower data scientists, engineers, and analysts to gain a deeper understanding of their data resources, streamline their workflows, and ensure data governance.</p><p>Ontologies give a semantic representation of the data elements within the domain. They can aid the data engineer in understanding how and why data elements are associated and improve processing pipelines. Ontologies also give data scientists context for model development and updating.</p><p>The technical and functional attributes of AI systems have been discussed. The next section discusses the different ways to implement systems in a modern cloud context. The use of cloud technology ensures that one can readily scale an AI system based on actual demand and provides for flexibility in resource allocations.</p><div><hr></div><h1>Modern AI deployment paradigms</h1><p>As AI systems continue to evolve, new deployment paradigms have emerged to address specific requirements and use cases. This section explores two significant approaches: cloud-native AI architectures and edge AI deployments.</p><h2>Cloud-native AI architectures</h2><p>The increasing complexity and scale of AI applications have led to the adoption of cloud-native architectures. These architectures leverage the scalability, flexibility, and cost-efficiency of cloud computing platforms to enable efficient development, deployment, and management of AI systems. In a cloud-native architecture, AI components are designed to run seamlessly in cloud environments, taking advantage of specialized services for storage, compute, and networking.</p><p>Key characteristics of cloud-native AI architectures include the following:</p><ul><li><p><strong>Containerization</strong>: AI applications are packaged into lightweight, portable containers using technologies such as Docker, ensuring consistency across development, testing, and production environments.</p></li><li><p><strong>Orchestration</strong>: Container orchestration platforms such as Kubernetes manage the deployment, scaling, and operation of application containers across clusters of hosts.</p></li><li><p><strong>Microservices</strong>: As discussed earlier, breaking down AI systems into smaller, independent services enables more efficient resource utilization and easier scaling.</p></li><li><p><strong>Serverless computing</strong>: Platforms such as AWS Lambda, Azure Functions, and Google Cloud Functions allow developers to focus on writing code without worrying about the underlying infrastructure, particularly useful for event-driven AI workloads.</p></li><li><p><strong>Managed services</strong>: Cloud providers offer specialized AI services such as fully managed machine learning platforms (e.g., Amazon SageMaker, Microsoft Azure ML, Google Vertex AI) that streamline the development and deployment process.</p></li><li><p><strong>Cloud-native versus lift-and-shift</strong>: Cloud-native AI components are specifically designed to leverage the benefits of cloud environments, such as auto-scaling, serverless computing, and managed services. This approach offers greater flexibility, scalability, and cost-efficiency compared to simply &#8220;lifting and shifting&#8221; existing on-premises AI systems to the cloud without architectural modifications.</p></li></ul><div><hr></div><h1>Data lakes and data warehouses in AI architectures: foundations for data-driven intelligence</h1><p>In the realm of AI, data is the cornerstone of innovation and progress. AI models thrive on massive volumes of data, leveraging it to learn patterns, make predictions, and generate valuable insights. However, effectively managing and harnessing the vast amounts of data involved in AI projects necessitates specialized storage and management solutions. Two prominent concepts that have emerged in this context are <strong>data lakes</strong> and <strong>data warehouses</strong>.</p><h2>Data lakes: a vast reservoir of raw data</h2><p>Data lakes serve as expansive repositories where raw data is stored in its native format. They are designed to accommodate structured, semi-structured, and unstructured data from diverse sources. The flexibility of data lakes makes them ideal for storing large volumes of data that may not have a predefined purpose or structure.</p><ul><li><p><strong>Key characteristics:</strong></p><ul><li><p><strong>Schema-on-read:</strong> Data lakes do not enforce a strict schema during ingestion, allowing for flexibility in data types and structures. The schema is defined during analysis or processing, empowering users to adapt to evolving data requirements.</p></li><li><p><strong>Cost-effective scalability:</strong> Data lakes can easily scale to accommodate growing data volumes, making them a cost-effective solution for storing massive datasets.</p></li><li><p><strong>Support for diverse data:</strong> Data lakes can handle a wide range of data, including sensor readings, social media feeds, log files, and more.</p></li><li><p><strong>Ideal for exploratory analysis:</strong> Data lakes provide a fertile ground for data scientists and analysts to explore data, identify patterns, and generate hypotheses.</p></li></ul></li><li><p><strong>Example use cases:</strong></p><ul><li><p>An e-commerce company might store clickstream data, customer reviews, and social media interactions in a data lake for subsequent analysis and personalization efforts.</p></li><li><p>A healthcare organization could use a data lake to store medical images, electronic health records, and genomic data for research and development of AI-driven diagnostic tools.</p></li></ul></li></ul><h2>Data warehouses: structured repositories for analytics</h2><p>Data warehouses are structured repositories that house processed and curated data, transformed into a consistent format for analysis and reporting purposes. One can build and develop ontologies to organize and provide semantic structure to the data that comes into the system. Ontologies also provide a mechanism to better manage and control model performance by making relationships between data elements explicit.</p><p>They excel at facilitating efficient querying and analysis, making them indispensable for business intelligence and decision support applications.</p><ul><li><p><strong>Key characteristics:</strong></p><ul><li><p><strong>Schema-on-write:</strong> Data warehouses enforce a predefined schema during data ingestion, ensuring data consistency and integrity.</p></li><li><p><strong>Optimized for querying:</strong> Data warehouses employ optimized data structures and indexing techniques to accelerate data retrieval and analysis, enabling faster insights.</p></li><li><p><strong>Support for structured data:</strong> Data warehouses are primarily designed for structured data, such as transactional data, customer information, and financial records.</p></li><li><p><strong>Ideal for business intelligence:</strong> Data warehouses empower organizations to generate reports, dashboards, and visualizations for informed decision-making.</p></li></ul></li><li><p><strong>Example use cases:</strong></p><ul><li><p>A financial institution might use a data warehouse to store transaction data, customer information, and market trends for risk analysis and fraud detection.</p></li><li><p>A manufacturing company could leverage a data warehouse to analyze production data, supply chain metrics, and customer feedback to optimize operations and improve product quality.</p></li></ul></li></ul><h2>The synergy of data lakes and data warehouses</h2><p>In many AI architectures, data lakes and data warehouses complement each other. Raw data is first ingested into a data lake, where it undergoes cleansing, transformation, and enrichment. The refined data is then transferred to a data warehouse for further analysis and reporting. This synergistic approach enables organizations to leverage the flexibility of data lakes for data exploration and the structure of data warehouses for decision support, creating a robust foundation for data-driven AI applications.</p><div><hr></div><h1>AI on cloud computing: a game-changer for AI</h1><p>The convergence of AI and cloud computing has opened up a new frontier of possibilities for organizations seeking to leverage the power of AI. Cloud computing provides a scalable, flexible, and cost-effective infrastructure for developing, deploying, and scaling AI applications. By harnessing the capabilities of the cloud, businesses can overcome the limitations of traditional on-premises AI solutions and accelerate innovation.</p><h2>Benefits of cloud-based AI</h2><p>Cloud-based AI offers several key advantages that make it an attractive option for organizations of all sizes:</p><ul><li><p><strong>Scalability</strong>: Cloud resources can be easily scaled up or down to meet the fluctuating demands of AI workloads. This elasticity allows organizations to handle large datasets, train complex models, and process vast amounts of data without having to invest in and maintain expensive hardware infrastructure.</p></li><li><p><strong>Flexibility</strong>: Cloud platforms provide a wide range of AI services and tools, giving organizations the flexibility to choose the best options for their specific needs. This allows businesses to experiment with different AI approaches, quickly iterate on models, and adapt to changing requirements.</p></li><li><p><strong>Cost-efficiency</strong>: Cloud-based AI can be more cost-effective than on-premises solutions. Organizations only pay for the resources they consume, eliminating the need for upfront capital investments in hardware and software. Additionally, cloud providers often offer pay-as-you-go pricing models, which can further reduce costs.</p></li></ul><p>By leveraging the power of cloud-based AI, organizations can unlock new levels of innovation, efficiency, and competitiveness.</p><h2>Major cloud AI platforms: accelerating innovation with comprehensive toolsets</h2><p>Major cloud providers have emerged as key players in the AI landscape, offering comprehensive suites of AI services and tools that cater to a wide range of needs. These platforms provide a one-stop shop for businesses and developers looking to leverage the power of AI in their applications and workflows.</p><h3>Key cloud AI platforms</h3><ul><li><p><strong>Google Cloud AI platform (Vertex AI)</strong>: This unified platform streamlines the entire <strong>Machine Learning</strong> (<strong>ML</strong>) lifecycle, from building and training models to deploying and managing them in production. Vertex AI&#8217;s AutoML feature simplifies model development for users with limited ML expertise, while the model garden offers a collection of pre-trained models ready for deployment. Vertex AI Pipelines orchestrates complex ML workflows, enabling efficient experimentation and automation.</p></li><li><p><strong>Amazon SageMaker</strong>: A fully managed service, SageMaker empowers users to build, train, and deploy ML models at scale. It boasts a wide array of built-in algorithms and frameworks, making it accessible to both beginners and experienced practitioners. SageMaker&#8217;s scalability and integration with other AWS services make it a popular choice for enterprise-grade AI solutions.</p></li><li><p><strong>Amazon Bedrock</strong>: This cutting-edge service democratizes access to <strong>Foundation Models</strong> (<strong>FM</strong>s) from leading AI start-ups and Amazon itself through a simple API. Bedrock enables developers to harness the power of state-of-the-art generative AI capabilities without having to build and train complex models from scratch.</p></li><li><p><strong>Microsoft Azure AI</strong>: This platform offers a diverse range of AI services, including pre-built AI models for computer vision, speech recognition, natural language processing, and decision-making. Azure Machine Learning allows users to create and deploy custom AI models, while the platform&#8217;s extensive integration with other Azure services makes it a versatile choice for a variety of AI applications.</p></li></ul><p>These cloud AI platforms provide a powerful and accessible way for organizations to incorporate AI into their operations, accelerating innovation and driving business value.</p><div><hr></div><h1>Summary</h1><p>In this chapter, we have explored the fundamental principles of AI system architecture, establishing a comprehensive framework for understanding the building blocks that power intelligent systems. We examined the core components &#8211; data as the lifeblood, algorithmic frameworks that enable learning, model architectures that encapsulate intelligence, and infrastructure that provides computational resources &#8211; along with architectural patterns such as microservices that offer modularity and flexibility. Critical design considerations of scalability, performance, reliability, and security were discussed as essential elements for robust AI systems that can grow with increasing demands while remaining resilient and protected.</p><p>The landscape of AI deployment continues to evolve rapidly, with cloud-native architectures leveraging containerization, orchestration, and serverless computing to achieve unprecedented efficiency. The synergy between data lakes, data warehouses, and data catalogs creates a solid foundation for data-driven intelligence, while major cloud platforms democratize access to sophisticated AI capabilities. As we move forward, these foundational principles will guide the development of AI systems that are not only powerful but also scalable, reliable, and secure &#8211; enabling the next generation of innovations across industries.</p><div><hr></div><h1>Relevant reading</h1><ul><li><p>Baheti, Radhakisan, and Helen Gill. &#8220;<a href="https://ieeecss.org/sites/ieeecss/files/2019-07/IoCT-Part3-02CyberphysicalSystems.pdf">Cyber-Physical Systems</a>.&#8221; <em>The Impact of Control Technology</em>, edited by Tariq Samad and Anuradha M. Annaswamy, IEEE Control Systems Society, 2011, pp. 161&#8211;66.</p></li><li><p>Bass, Len, Paul Clements, and Rick Kazman. <em>Software Architecture in Practice</em>. 3rd ed., Addison-Wesley, 2012.</p></li><li><p>Hazelwood, Kim, et al. &#8220;<a href="https://ieeexplore.ieee.org/document/8327042">Applied Machine Learning at Facebook: A Datacenter Infrastructure Perspective</a>.&#8221; <em>2018 IEEE International Symposium on High Performance Computer Architecture (HPCA)</em>, IEEE, 2018, pp. 620&#8211;629.</p></li><li><p>LeCun, Yann, Yoshua Bengio, and Geoffrey Hinton. &#8220;<a href="https://www.nature.com/articles/nature14539">Deep Learning</a>.&#8221; <em>Nature</em>, vol. 521, no. 7553, 2015, pp. 436&#8211;44, doi.org/10.1038/nature14539.</p></li><li><p>Mao, Hongzi, et al. &#8220;<a href="https://www.microsoft.com/en-us/research/publication/resource-management-deep-reinforcement-learning">Resource Management with Deep Reinforcement Learning.</a>&#8221; <em>Proceedings of the 15th ACM Workshop on Hot Topics in Networks (HotNets-XV)</em>, ACM, 2016, pp. 50&#8211;56, people.csail.mit.edu/alizadeh/papers/deeprm-hotnets16.pdf.</p></li><li><p>National Institute of Standards and Technology. <em><a href="https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-ai-rmf-10">Artificial Intelligence Risk Management Framework</a> (AI RMF 1.0).</em> NIST, 2023.</p></li><li><p>Patterson, David, et al. &#8220;<a href="https://arxiv.org/abs/2104.10350">Carbon Emissions and Large Neural Network Training</a>.&#8221; <em>arXiv</em>, 2021, arxiv.org/abs/2104.10350.</p></li><li><p>Sculley, D., et al. &#8220;<a href="https://proceedings.neurips.cc/paper/2015/file/86df7dcfd896fcaf2674f757a2463eba-Paper.pdf">Hidden Technical Debt in Machine Learning Systems.</a>&#8221; <em>Advances in Neural Information Processing Systems</em>, vol. 28, 2015, pp. 2503&#8211;2511.</p></li></ul><div><hr></div><p>To go deeper on designing robust, scalable AI-enabled systems&#8212;from integrating machine learning into existing architectures to managing risks like underperformance, cost overruns, and operational complexity&#8212;check out <em><strong><a href="https://www.packtpub.com/en-us/product/architecting-ai-software-systems-9781804619469">Architecting AI Software Systems</a></strong></em> by <strong>Richard D Avila</strong> and <strong>Imran Ahmad</strong> (Packt, 2025). Through a structured progression of architectural concepts, real-world case studies, and hands-on exercises (including a fictional AI-enabled system you can dissect end to end), it shows software and systems architects, CTOs, VPs of Engineering, AI/ML engineers, and developers how to select the right models and data pipelines, use architectural models to ensure cohesion, simulate and optimize AI performance through iteration, and apply patterns and heuristics to integrate AI into large-scale systems with strong user experience and performance&#8212;so you can confidently architect AI-driven products across a range of domains.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.packtpub.com/en-us/product/architecting-ai-software-systems-9781804619469" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3POB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2305437-423b-4c1c-a6f4-e3dab69fc532_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!3POB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2305437-423b-4c1c-a6f4-e3dab69fc532_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!3POB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2305437-423b-4c1c-a6f4-e3dab69fc532_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!3POB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2305437-423b-4c1c-a6f4-e3dab69fc532_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3POB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2305437-423b-4c1c-a6f4-e3dab69fc532_2250x2775" width="312" height="384.85714285714283" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d2305437-423b-4c1c-a6f4-e3dab69fc532_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:312,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Architecting AI Software Systems&quot;,&quot;title&quot;:&quot;Architecting AI Software Systems&quot;,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.packtpub.com/en-us/product/architecting-ai-software-systems-9781804619469&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Architecting AI Software Systems" title="Architecting AI Software Systems" srcset="https://substackcdn.com/image/fetch/$s_!3POB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2305437-423b-4c1c-a6f4-e3dab69fc532_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!3POB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2305437-423b-4c1c-a6f4-e3dab69fc532_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!3POB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2305437-423b-4c1c-a6f4-e3dab69fc532_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!3POB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2305437-423b-4c1c-a6f4-e3dab69fc532_2250x2775 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here&#8217;s what some readers have said:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aZpm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd06f8ef9-65e6-43a3-a492-bb9478950297_853x802.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aZpm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd06f8ef9-65e6-43a3-a492-bb9478950297_853x802.png 424w, https://substackcdn.com/image/fetch/$s_!aZpm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd06f8ef9-65e6-43a3-a492-bb9478950297_853x802.png 848w, https://substackcdn.com/image/fetch/$s_!aZpm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd06f8ef9-65e6-43a3-a492-bb9478950297_853x802.png 1272w, https://substackcdn.com/image/fetch/$s_!aZpm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd06f8ef9-65e6-43a3-a492-bb9478950297_853x802.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aZpm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd06f8ef9-65e6-43a3-a492-bb9478950297_853x802.png" width="853" height="802" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d06f8ef9-65e6-43a3-a492-bb9478950297_853x802.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:802,&quot;width&quot;:853,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:192721,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/180580239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd06f8ef9-65e6-43a3-a492-bb9478950297_853x802.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!aZpm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd06f8ef9-65e6-43a3-a492-bb9478950297_853x802.png 424w, https://substackcdn.com/image/fetch/$s_!aZpm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd06f8ef9-65e6-43a3-a492-bb9478950297_853x802.png 848w, https://substackcdn.com/image/fetch/$s_!aZpm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd06f8ef9-65e6-43a3-a492-bb9478950297_853x802.png 1272w, https://substackcdn.com/image/fetch/$s_!aZpm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd06f8ef9-65e6-43a3-a492-bb9478950297_853x802.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KWHP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540c6d8-b754-46af-b453-c37a646ec2e9_851x753.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KWHP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540c6d8-b754-46af-b453-c37a646ec2e9_851x753.png 424w, https://substackcdn.com/image/fetch/$s_!KWHP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540c6d8-b754-46af-b453-c37a646ec2e9_851x753.png 848w, https://substackcdn.com/image/fetch/$s_!KWHP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540c6d8-b754-46af-b453-c37a646ec2e9_851x753.png 1272w, https://substackcdn.com/image/fetch/$s_!KWHP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540c6d8-b754-46af-b453-c37a646ec2e9_851x753.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KWHP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540c6d8-b754-46af-b453-c37a646ec2e9_851x753.png" width="851" height="753" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5540c6d8-b754-46af-b453-c37a646ec2e9_851x753.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:753,&quot;width&quot;:851,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:151425,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/180580239?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540c6d8-b754-46af-b453-c37a646ec2e9_851x753.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!KWHP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540c6d8-b754-46af-b453-c37a646ec2e9_851x753.png 424w, https://substackcdn.com/image/fetch/$s_!KWHP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540c6d8-b754-46af-b453-c37a646ec2e9_851x753.png 848w, https://substackcdn.com/image/fetch/$s_!KWHP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540c6d8-b754-46af-b453-c37a646ec2e9_851x753.png 1272w, https://substackcdn.com/image/fetch/$s_!KWHP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540c6d8-b754-46af-b453-c37a646ec2e9_851x753.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Implicit Memory Systems for LLMs]]></title><description><![CDATA[When Code Surrenders to Context]]></description><link>https://deepengineering.substack.com/p/implicit-memory-systems-for-llms</link><guid isPermaLink="false">https://deepengineering.substack.com/p/implicit-memory-systems-for-llms</guid><dc:creator><![CDATA[Sam Keen]]></dc:creator><pubDate>Wed, 26 Nov 2025 08:42:07 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/a56371ea-f985-445a-8a3f-6125fb18bee5_1280x731.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>The Irony of Explicit Memory Controls</strong></h2><p>In my previous post, <a href="https://alteredcraft.com/p/the-memory-illusion-teaching-your">The Memory Illusion</a>, I demonstrated that LLM memory doesn&#8217;t require vector databases or sophisticated architectures. It&#8217;s fundamentally just text management. We built a proof-of-concept in ~150 lines of Python that stored memories in a simple markdown file. It worked. But it had an amusing limitation: <strong>The user had to remember to tell the AI to remember</strong>.</p><p>The system required explicit commands. Want the LLM to store your name? Type <code>!remember &#8220;My name is Alex&#8221;</code>. Want it to know your project preferences? Another <code>!remember</code> command. The irony was sharp: we&#8217;d outsourced memory to technology, only to burden ourselves with managing that memory manually.</p><p>This wasn&#8217;t an oversight. It was a conscious design decision. Our application code controlled every memory operation through explicit if/else logic. The host app was the memory manager, and the LLM was simply our text processor.</p><p>But what if we handed that authority to the LLM itself?</p><p>This isn&#8217;t an incremental improvement or &#8220;v2&#8221; of the same approach. It&#8217;s a fundamentally different philosophy: <strong>trusting the LLM to autonomously manage its own memory</strong>. The technical implications are profound. We move from programming specific behaviors to setting high-level intentions. We shift from writing parsing logic to defining trust boundaries.</p><p>This approach enables the system to handle its own errors, organize information without explicit rules, and maintain its own memory hygiene. All without writing a single if/else statement.</p><h2><strong>The Explicit Approach: When Code Defines Every Decision</strong></h2><p>In my original POC, every memory operation required explicit user commands. Here&#8217;s what a typical session looked like:</p><pre><code><code>[You]: Hello, I&#8217;m working on a React app
[Claude]: Hi! What kind of React app are you building?
[You]: !remember I am building a React e-commerce application
[Claude]: [Memory saved]</code></code></pre><p>Here we see that required use of <code>!remember</code>. Adding to the user&#8217;s cognitive load alongside their actual work.</p><p>Behind the scenes, our code intercepted every message, parsed for commands, managed file operations, and reconstructed the prompt with memories for each interaction. We were the brain; the LLM was just processing text within our constraints.</p><p>This gave us complete control. We defined in code the exact format of the memory file. Want memories timestamped? We coded it. Want them categorized? More code. Every behavior was explicit, predictable, testable.</p><p>This is how we&#8217;ve built software applications since the inception of the craft. We write the logic, we define the control flow, we handle the edge cases. It&#8217;s comfortable, familiar territory. But when working with LLMs, this traditional approach means we&#8217;re not fully leveraging what makes them truly powerful: their ability to understand context and make intelligent decisions autonomously.</p><p>The LLM&#8217;s contextual intelligence sits idle while our code makes every decision. This intelligence was trained on billions of examples of how humans organize and retrieve information.</p><p>Most importantly, users had to remember the commands, creating friction in your app&#8217;s usability. Edge cases multiplied. The code grew ever larger as we handled more scenarios, more commands, more special cases. We were swimming upstream against the fundamental capabilities of modern LLMs.</p><h2><strong>The Implicit Approach: LLM as Autonomous Manager</strong></h2><p>The paradigm shift is what matters. We&#8217;re implementing a harness that grants the LLM autonomous authority. While this example uses the Claude Agent SDK, the pattern can be implemented with other SDKs or custom code. The key is delegation of decision-making, not specific tooling.</p><p>Let&#8217;s look at the skeleton of our memory tool implementation:<br>Full code found in the <a href="https://github.com/AlteredCraft/implicit-memory-system-poc-article/tree/main">companion app</a></p><pre><code><code># memory_tool.py 
class LocalFilesystemMemoryTool(BetaAbstractMemoryTool):
    &#8220;&#8221;&#8220;
    The LLM calls these methods autonomously based on context.
    We provide the infrastructure; Claude makes the decisions.
    &#8220;&#8221;&#8220;

    # The hook methods (memory tools) Claude is made aware of:

    @override
    def view(self, command):
        &#8220;&#8221;&#8220;Claude calls this to read memories or list files&#8221;&#8220;&#8221;
        # Validate path, read file/directory, return contents

    @override
    def create(self, command):
        &#8220;&#8221;&#8220;Claude calls this to create new memory files&#8221;&#8220;&#8221;
        # Validate path, write file, log operation

    @override
    def str_replace(self, command):
        &#8220;&#8221;&#8220;Claude calls this to update existing memories&#8221;&#8220;&#8221;
        # Find text, replace it, handle errors

    @override
    def insert(self, command):
        &#8220;&#8221;&#8220;Claude calls this to add lines to memories&#8221;&#8220;&#8221;
        # Insert at specific line number

    @override
    def delete(self, command):
        &#8220;&#8221;&#8220;Claude calls this to remove memories&#8221;&#8220;&#8221;
        # Delete files or directories

    @override
    def rename(self, command):
        &#8220;&#8221;&#8220;Claude calls this to reorganize memories&#8221;&#8220;&#8221;
        # Move or rename files</code></code></pre><p>The SDK provides these hook methods as the interface contract. We implement the file operations; Claude decides when to invoke them. No command parsing required from us.</p><p>The system prompt grants authority. Instead of telling Claude what to remember, we grant it authority:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UbAb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6971d63d-d3ad-4258-b6cb-10527eaf33ec_1144x276.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UbAb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6971d63d-d3ad-4258-b6cb-10527eaf33ec_1144x276.png 424w, https://substackcdn.com/image/fetch/$s_!UbAb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6971d63d-d3ad-4258-b6cb-10527eaf33ec_1144x276.png 848w, https://substackcdn.com/image/fetch/$s_!UbAb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6971d63d-d3ad-4258-b6cb-10527eaf33ec_1144x276.png 1272w, https://substackcdn.com/image/fetch/$s_!UbAb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6971d63d-d3ad-4258-b6cb-10527eaf33ec_1144x276.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UbAb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6971d63d-d3ad-4258-b6cb-10527eaf33ec_1144x276.png" width="1144" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6971d63d-d3ad-4258-b6cb-10527eaf33ec_1144x276.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1144,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:54239,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://exploregamedev.substack.com/i/178638297?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6971d63d-d3ad-4258-b6cb-10527eaf33ec_1144x276.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!UbAb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6971d63d-d3ad-4258-b6cb-10527eaf33ec_1144x276.png 424w, https://substackcdn.com/image/fetch/$s_!UbAb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6971d63d-d3ad-4258-b6cb-10527eaf33ec_1144x276.png 848w, https://substackcdn.com/image/fetch/$s_!UbAb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6971d63d-d3ad-4258-b6cb-10527eaf33ec_1144x276.png 1272w, https://substackcdn.com/image/fetch/$s_!UbAb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6971d63d-d3ad-4258-b6cb-10527eaf33ec_1144x276.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">Concise system prompt</figcaption></figure></div><p>This shift is profound. We&#8217;re not programming behaviors anymore. We&#8217;re setting intentions and trusting Claude to execute them. The orchestration that once consumed lines of code now happens autonomously, guided by our system prompt.</p><p>This hands-off approach of providing just the infrastructure hooks while delegating all decision-making to the LLM may seem minimal. That&#8217;s precisely the point.</p><p>If you&#8217;ve spent years writing deterministic code, this delegation feels uncomfortable. You&#8217;re trusting the LLM to make architectural decisions you once controlled. That discomfort is valid. Let&#8217;s add some clarity to the real world implications of this approach.</p><h2><strong>Under the Hood: Autonomous Decisions in Action</strong></h2><p>Let&#8217;s trace what actually happens during a conversation. These examples come from real sessions the <a href="https://github.com/AlteredCraft/implicit-memory-system-poc/">companion app</a>&#8217;s trace and render diagram features.</p><h3><strong>Creating New Memory</strong></h3><pre><code><code>[You]: &#8220;I&#8217;m starting a new project. I&#8217;d like to build a web app for solo entrepreneurs
       to track their time and projects&#8221;

~Claude internally~: Creates new memory at path `project_solo_entrepreneur_app.txt`

[Claude]: &#8220;That&#8217;s an exciting project! A time and project tracker specifically for solo entrepreneurs ...&#8221;</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IUyu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edbd534-97ba-45a2-a53a-2194562e7a4e_3958x2438.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IUyu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edbd534-97ba-45a2-a53a-2194562e7a4e_3958x2438.png 424w, https://substackcdn.com/image/fetch/$s_!IUyu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edbd534-97ba-45a2-a53a-2194562e7a4e_3958x2438.png 848w, https://substackcdn.com/image/fetch/$s_!IUyu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edbd534-97ba-45a2-a53a-2194562e7a4e_3958x2438.png 1272w, https://substackcdn.com/image/fetch/$s_!IUyu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edbd534-97ba-45a2-a53a-2194562e7a4e_3958x2438.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IUyu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edbd534-97ba-45a2-a53a-2194562e7a4e_3958x2438.png" width="1456" height="897" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2edbd534-97ba-45a2-a53a-2194562e7a4e_3958x2438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:897,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:474522,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://exploregamedev.substack.com/i/178638297?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edbd534-97ba-45a2-a53a-2194562e7a4e_3958x2438.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!IUyu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edbd534-97ba-45a2-a53a-2194562e7a4e_3958x2438.png 424w, https://substackcdn.com/image/fetch/$s_!IUyu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edbd534-97ba-45a2-a53a-2194562e7a4e_3958x2438.png 848w, https://substackcdn.com/image/fetch/$s_!IUyu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edbd534-97ba-45a2-a53a-2194562e7a4e_3958x2438.png 1272w, https://substackcdn.com/image/fetch/$s_!IUyu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edbd534-97ba-45a2-a53a-2194562e7a4e_3958x2438.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The sequence of actions resulting in the creation of a new memory</figcaption></figure></div><p>You didn&#8217;t ask Claude to remember anything. Claude autonomously decided this information was worth preserving. It even made an architectural decision. It created a new project file rather than appending to a general profile. These are judgment calls the LLM now makes without any explicit rules from us beyond guidance in the system prompt.</p><h3><strong>Self-Correction</strong></h3><p>What happens when the LLM makes a mistake? In traditional systems, errors bubble up to users. Here&#8217;s how autonomous authority changes that dynamic:</p><pre><code><code>[You]: &#8220;Let&#8217;s add these MVP features: client profiles, task tracking, time logging&#8221;

~Claude internally~: Tries to update `solo_entrepreneur.txt`,
gets FileNotFoundError, checks memory folder, finds correct
filename `project_solo_entrepreneur_app.txt`, successfully updates

[Claude]: &#8220;Got it! I&#8217;ve noted those MVP features for your project.&#8221;</code></code></pre><p>Claude made a mistake with the filename, detected the error, diagnosed the issue, and self-corrected. All autonomously. No error messages to the user, no manual intervention required.</p><p>Our code deliberately re-raises exceptions to Claude rather than hiding them. This design turns errors into recovery opportunities. Claude receives the FileNotFoundError, understands what went wrong, and adapts its strategy. This resilience emerges naturally from giving the LLM authority rather than rigid rules.</p><h3><strong>Context-Aware Retrieval</strong></h3><p>The LLM decides <em>when</em> to check memories. No rules tell it to look for context on greetings, it just understands that&#8217;s useful:</p><pre><code><code>[You]: &#8220;hello&#8221;

~Claude internally~: Checks memory folder, finds and reads
`user_preferences.txt` containing &#8220;Vacationing at the beach
as of 2025-11-03&#8221;

[Claude]: &#8220;Hi! How are things going at the beach today?&#8221;</code></code></pre><p>Claude found relevant information and used it naturally in conversation.</p><p>These autonomous behaviors all emerge without us programming them explicitly. They include deciding on storage structure, recovering from errors, and retrieving contextual information.</p><div class="pullquote"><p><em><strong>We implement the hooks; Claude provides the intelligence.</strong></em></p></div><h2><strong>Code vs. Prompts: Where Control Lives</strong></h2><p>The shift to implicit memory doesn&#8217;t mean abandoning all control. It means being strategic about where that control resides. By examining what stays in code versus what moves to prompts, we can understand the architecture of trust in AI systems.</p><h3><strong>Hard Boundaries in Code</strong></h3><p>The <code>memory_tool.py</code> file shows what explicit choices we&#8217;re still making in code:</p><p><strong>Security Boundaries</strong>: The <code>_validate_path()</code> method explicitly ensures all operations stay within the <code>/memories</code> directory. This is hard-coded protection against path traversal attacks. It&#8217;s a security boundary we enforce in code, not through prompts.</p><p><strong>Logging &amp; Tracing</strong>: Every operation is explicitly logged for debugging and audit trails. We know every tool call Claude made to the memory system and what was read or written.</p><p>These coded constraints are examples of the guardrails we build to create a safe sandbox within which Claude operates autonomously. They&#8217;re guarantees enforced by our infrastructure, not suggestions in a prompt. In a production system you will expand on these guardrails until you have an acceptable risk level for your line of business.</p><p><strong>The Power of Hooks: Beyond File Persistence</strong></p><p>The SDK&#8217;s hook-based architecture (<a href="https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/lib/tools/_beta_builtin_memory_tool.py">BetaAbstractMemoryTool</a>) enables remarkable flexibility. While our implementation uses a filesystem backend, you could implement these same hooks to:</p><ul><li><p>Store memories in a PostgreSQL database for multi-user applications</p></li><li><p>Use Redis for high-performance, distributed memory systems</p></li><li><p>Implement vector embeddings for semantic memory retrieval</p></li><li><p>Create hybrid systems that combine multiple storage backends</p></li></ul><p>The LLM doesn&#8217;t care about the backend. It just calls the hooks and trusts the implementation to handle the details. This separation of concerns enables powerful architectural flexibility while maintaining the same autonomous decision-making paradigm.</p><h3><strong>Guidance Through Prompts</strong></h3><p>Between the hard constraints of code and the full autonomy of the LLM lies prompt-based guidance. These are strong suggestions that shape behavior without guarantees:</p><pre><code><code>Keep your memory folder organized. Update existing files rather
than creating duplicates. Include metadata like dates when relevant.

When the memory folder exceeds 20 files, consolidate related
memories into broader topic files. Archive outdated information
with clear labels.</code></code></pre><p>This achieves implicit memory maintenance without explicit commands such as <code>!compact</code>. The LLM monitors its own memory health and performs housekeeping autonomously.</p><p>For more structure in the memory files, we could specify a frontmatter format for all new memories. This creates a sort of pseudo-schema to be leveraged by the LLM and our instrumentation code:</p><pre><code><code>All memory files should start with:
---
created: YYYY-MM-DD
relevance: current|archive
---</code></code></pre><p>This gives Claude a framework without removing its autonomy. It can still make contextual decisions within these guidelines.</p><p><strong>Making the Implicit Explicit: Explanatory Mode</strong></p><p>For demonstration purposes in the companion chat app, I added <strong>explanatory versions of the prompts</strong>. These make the implicit decision-making visible for learning:</p><pre><code><code># Directive in the system prompt

IMPORTANT: You are a teaching tool to help the user understand how you
process and utilize memories. After each natural conversational response,
add a concise summary of the memory actions you took and why.</code></code></pre><p>This produces illuminating results:</p><pre><code><code>[You]: &#8220;ok, I think I&#8217;ll set a goal to finish the outline today, by noon&#8221;

[Claude]: &#8220;Excellent goal! Finishing the outline by noon today (2025-11-10) gives ...&#8221;

---
Memory Actions Taken:
- Updated article status to reflect current goal: completing the outline by noon today (2025-11-10)
- This helps track progress and provides context if we discuss the article later today or in future sessions</code></code></pre><p>This explanatory mode becomes a powerful tool for understanding the LLM&#8217;s approach to memory and tuning your system prompts. It transforms the black box into a glass box, revealing the autonomous reasoning. Use it to validate that your prompt guidance is achieving the intended behaviors, then switch back to normal operation once satisfied.</p><p>The beauty of this division is that higher risk critical decisions stay in code where they&#8217;re guaranteed, while contextual decisions that benefit from intelligence and flexibility live in prompts. As models improve, the prompt-based behaviors get smarter automatically, while our security boundaries remain firm.</p><h2><strong>Conclusion: Learning from Scale</strong></h2><p>There&#8217;s a principle in AI research that keeps proving itself true. Systems built on general methods and scaled computation consistently outperform those with hand-crafted rules. Rich Sutton calls this <a href="http://www.incompleteideas.net/IncIdeas/BitterLesson.html">&#8220;The Bitter Lesson&#8221;</a>. Bitter because it means our clever, specialized solutions inevitably lose to simpler approaches that leverage raw intelligence.</p><p>The shift from explicit to implicit memory perfectly illustrates this principle. And it reveals what becomes possible when we stop fighting it.</p><h3><strong>The Power of Delegated Intelligence</strong></h3><p>We&#8217;ve seen the behaviors that emerge: autonomous memory creation, contextual reorganization, self-healing from errors. These weren&#8217;t programmed. They emerged from granting the LLM authority within safe boundaries.</p><p>This pattern extends beyond memory. It extends to complex decision-making in your application. Routing requests, organizing data, managing workflows. All can potentially be delegated to intelligence rather than encoded in logic. The infrastructure you build becomes a framework for capabilities you haven&#8217;t even imagined yet.</p><p><strong>Hybrid model</strong>: Codify workflows that need precise control as tools, then let the LLM autonomously decide when and how to use them. This gives you explicit control over critical operations while still leveraging the LLM&#8217;s decision-making for orchestration.</p><p><strong>As models improve, your system automatically gets better</strong>. When the next generation releases, you update one parameter. Your existing infrastructure suddenly makes smarter decisions. No refactoring. No new edge cases. The same hooks you implement today become more capable tomorrow.</p><h3><strong>Trust but Verify: Your Implementation Philosophy</strong></h3><p>This shift changes how we architect AI systems. Instead of writing decision trees, we adopt a &#8220;trust but verify&#8221; philosophy:</p><ul><li><p><strong>Trust</strong>: Grant the AI authority through system prompts</p></li><li><p><strong>Verify</strong>: Monitor the behaviors that emerge</p></li><li><p><strong>Guide</strong>: Adjust prompts based on observed patterns</p></li><li><p><strong>Iterate</strong>: Refine boundaries as models improve</p></li></ul><p>We&#8217;re reallocating the effort once spent on explicit control logic to validation and evaluation. Same total engineering effort, fundamentally better product. The interesting work moves from implementing specific behaviors to designing systems that exhibit emergent intelligence while maintaining appropriate guardrails.</p><h3><strong>The Path Forward</strong></h3><p>But here&#8217;s my challenge to you: <strong>run an experiment</strong>. The gap between what models can do and what we think they can do is often surprising. Many teams discover their explicit controls were solving problems the LLM could handle autonomously (and often better).</p><p><strong>You can test this today.</strong> The <a href="https://github.com/AlteredCraft/implicit-memory-system-poc-article/tree/main">companion app</a> provides a complete learning laboratory:</p><ul><li><p>Multiple prompting strategies for testing delegation</p></li><li><p>Full session tracing of every memory decision</p></li><li><p>Visual sequence diagrams exported from conversations</p></li><li><p>Real-time observation of memory folder organization</p></li></ul><p>Clone it. Run it. Watch what emerges. Add your own prompts. Test your assumptions. Even if the model isn&#8217;t ready for your use case today, you&#8217;ll have the framework when the next version drops.</p><p>The original memory post showed that LLM memory is just text management. This exploration reveals a deeper pattern: <strong>we&#8217;re moving from programming behaviors to orchestrating capabilities</strong>. The question isn&#8217;t whether to trust AI with decisions. It&#8217;s understanding which decisions, with what boundaries, and how to monitor the results.</p><p>The tools are ready. The models are capable. The only thing standing between you and implicit memory is running that first experiment.</p><div><hr></div><p><strong>Want to explore further?</strong></p><ul><li><p>Original approach: <a href="https://github.com/AlteredCraft/simple_llm_memory_poc">simple_llm_memory_poc</a></p></li><li><p>Implicit approach: <a href="https://github.com/AlteredCraft/implicit-memory-system-poc-article/tree/main">implicit-memory-system-poc</a></p></li><li><p>Claude Agent SDK: <a href="https://docs.claude.com/en/api/agent-sdk/overview">Documentation</a></p></li><li><p>Memory Tool: <a href="https://docs.claude.com/en/docs/agents-and-tools/tool-use/memory-tool">Official docs</a></p></li><li><p>Anthropic on Agents: <a href="https://www.anthropic.com/engineering/building-effective-agents">Building Effective Agents</a></p></li></ul><div><hr></div><p><strong>About the Author</strong>: Sam Keen publishes at at <a href="https://alteredcraft.com/">AlteredCraft</a>. Subscribe for content that dives into the paradigm shifts AI brings to software development in addition to a free weekly roundup of the latest AI Tutorials, Tool, and News relevant to software developers.</p>]]></content:encoded></item><item><title><![CDATA[Merkle Trees and Anti-Entropy — Concepts and Implementation]]></title><description><![CDATA[How Distributed Systems Like Cassandra and DynamoDB Stay Consistent at Scale &#8212; And How You Can Build It Too]]></description><link>https://deepengineering.substack.com/p/merkle-trees-and-anti-entropy-concepts</link><guid isPermaLink="false">https://deepengineering.substack.com/p/merkle-trees-and-anti-entropy-concepts</guid><dc:creator><![CDATA[Archit Agarwal]]></dc:creator><pubDate>Mon, 24 Nov 2025 06:22:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!wBKQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever gone on a road trip with friends and tried to split expenses? It&#8217;s fun &#8212; until someone gets stuck doing the dirty work of tracking who owes what. Picture this: you&#8217;re in a group of five, each person tallying their own totals. When it&#8217;s time to settle up, you don&#8217;t want to compare receipts line by line with everyone, every day. Instead, you check the overall totals. If someone&#8217;s number doesn&#8217;t match, you know there&#8217;s a discrepancy &#8212; so you only compare details with the friend who has a different total. Efficient, right?</p><p>Now, scale up this problem. Imagine a massive distributed system where 10GB of data lives on each node, and every bit is replicated across five different nodes for reliability. When a network partition occurs and a node comes back online, is it really practical to sync all 10GB of data across every node, every time? Of course not.</p><p>This is the very challenge that engineers at Cassandra, DynamoDB, and even in blockchain technology have faced. How do you quickly identify and fix just the pieces of data that have changed, without wasting massive amounts of time and bandwidth checking everything, everywhere?</p><p>The secret lies in two powerful concepts: Merkle trees and anti-entropy protocols.</p><p>In this article, we&#8217;ll step into an engineer&#8217;s shoes &#8212; exploring how these data structures and algorithms allow distributed databases like DynamoDB and Cassandra to efficiently detect, compare, and synchronize changes. We&#8217;ll break down what Merkle trees and anti-entropy actually are, why they matter, and how you can implement them yourself (with hands-on examples in Golang).</p><p><em>Ready to see how modern distributed systems stay fast and consistent, even at a massive scale? Let&#8217;s dive in!</em></p><h2><strong>Understanding Merkle Trees</strong></h2><p>Imagine you&#8217;re the head of accounting at a global company with offices in 100 cities worldwide. Your responsibility: keep track of all expenses across every location. Reconciling records for every single office, every time, would be a logistical nightmare &#8212; not to mention a massive waste of time and resources.</p><p>To solve this, you create a smarter system:</p><ul><li><p>Instead of checking every office one by one, you appoint an assistant who keeps a running total.</p></li><li><p>Now, you only compare your assistant&#8217;s master total with your own. If the totals match, you&#8217;re in sync. If not, you zero in on where the change happened.</p></li><li><p>Taking it further, you group offices by regions (say, Asia-Pacific and USA). Each region reports upward. When there&#8217;s a mismatch, you drill down region by region, then country, then city.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wBKQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wBKQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic 424w, https://substackcdn.com/image/fetch/$s_!wBKQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic 848w, https://substackcdn.com/image/fetch/$s_!wBKQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic 1272w, https://substackcdn.com/image/fetch/$s_!wBKQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wBKQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic" width="720" height="480" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:480,&quot;width&quot;:720,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:34582,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/178496085?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wBKQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic 424w, https://substackcdn.com/image/fetch/$s_!wBKQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic 848w, https://substackcdn.com/image/fetch/$s_!wBKQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic 1272w, https://substackcdn.com/image/fetch/$s_!wBKQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd0f1af4-5d95-4113-b880-6825c707b3b9_720x480.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This hierarchical, divide-and-narrow approach is precisely how Merkle trees work in distributed systems.</p><h2><strong>What is a Merkle Tree?</strong></h2><p>A Merkle tree is a data structure designed for efficient and secure verification of large sets of data. Here&#8217;s the basic anatomy:</p><ul><li><p><strong>Leaf Nodes:</strong> Each one holds a hash of actual data &#8212; like an office&#8217;s transactions.</p></li><li><p><strong>Internal (Branch) Nodes:</strong> Each combines and hashes its children&#8217;s hashes, summarizing everything below.</p></li><li><p><strong>Root Node:</strong> The master hash &#8212; changing if anything underneath changes.</p></li></ul><p><em>Key insight:</em> Instead of comparing all data, you compare hashes at each level, honing in only where there&#8217;s a mismatch. This scales logarithmically, not linearly.</p><p><strong>Historical Context:</strong><br>Devised by Ralph Merkle in 1979, Merkle trees are now central to blockchains, distributed file systems, and database consistency.</p><h2><strong>How Merkle Trees Work</strong></h2><h3><strong>a. The Hashing Process</strong></h3><ul><li><p><strong>Hashing the Leaves:</strong> Pass each data item (file, transaction, etc.) through a hash function (e.g., SHA-256). These are the leaf nodes.</p></li><li><p><strong>Building Up the Tree:</strong> Pair up the leaf hashes, concatenate and hash again to form parents, repeating until you reach the root.</p></li><li><p><strong>Checking for Changes:</strong> Any single data change changes its hash and all parents up to the root. Comparing roots lets you instantly check if two datasets match.</p></li></ul><h3><strong>b. Example: Tiny Merkle Tree</strong></h3><p>Suppose you have four data blocks: A, B, C, D.<br><strong>Step 1:</strong> Hash each block.<br><strong>Step 2:</strong> Hash pairs: (A+B), (C+D).<br><strong>Step 3:</strong> Hash those two: (AB + CD) = Root.<br>If just B changes, only three hashes need recalculating, and you can precisely spot the change.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dJP-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc87e9ee-c847-4ef1-a8a6-f21493210d72_720x1080.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dJP-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc87e9ee-c847-4ef1-a8a6-f21493210d72_720x1080.heic 424w, https://substackcdn.com/image/fetch/$s_!dJP-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc87e9ee-c847-4ef1-a8a6-f21493210d72_720x1080.heic 848w, https://substackcdn.com/image/fetch/$s_!dJP-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc87e9ee-c847-4ef1-a8a6-f21493210d72_720x1080.heic 1272w, https://substackcdn.com/image/fetch/$s_!dJP-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc87e9ee-c847-4ef1-a8a6-f21493210d72_720x1080.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dJP-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc87e9ee-c847-4ef1-a8a6-f21493210d72_720x1080.heic" width="720" height="1080" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc87e9ee-c847-4ef1-a8a6-f21493210d72_720x1080.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1080,&quot;width&quot;:720,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:38664,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/178496085?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc87e9ee-c847-4ef1-a8a6-f21493210d72_720x1080.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dJP-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc87e9ee-c847-4ef1-a8a6-f21493210d72_720x1080.heic 424w, https://substackcdn.com/image/fetch/$s_!dJP-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc87e9ee-c847-4ef1-a8a6-f21493210d72_720x1080.heic 848w, https://substackcdn.com/image/fetch/$s_!dJP-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc87e9ee-c847-4ef1-a8a6-f21493210d72_720x1080.heic 1272w, https://substackcdn.com/image/fetch/$s_!dJP-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc87e9ee-c847-4ef1-a8a6-f21493210d72_720x1080.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Real-World Applications of Merkle Trees</strong></h2><ul><li><p><strong>Blockchains:</strong> Store all transactions for a block as a Merkle tree. The root in the block header allows anyone to verify a transaction&#8217;s inclusion.</p></li><li><p><strong>Versioned File Systems (e.g., Git):</strong> Every commit is represented as a Merkle root; differences between commits highlight only what&#8217;s changed.</p></li><li><p><strong>Distributed Databases (Cassandra, DynamoDB):</strong> Use Merkle trees for anti-entropy. Only out-of-sync segments are reconciled, not the full dataset.</p></li></ul><p><em>Merkle trees guarantee tamper-evidence and make large-scale, bandwidth-efficient consistency possible.</em></p><h2><strong>What is Anti-Entropy?</strong></h2><p>Back to our global accounts team: over time, small differences creep into each office&#8217;s ledger &#8212; network delays, miscommunications, independent corrections. These inconsistencies, or entropy, must be regularly tracked down and fixed.<br>Anti-entropy is your systematic, efficient approach to reconciling just the differences, not everything.</p><h2><strong>Anti-Entropy Mechanisms</strong></h2><ul><li><p><strong>Gossip Protocols:</strong> Random offices synchronize with each other, gradually spreading updates network-wide.</p></li><li><p><strong>Vector Clocks:</strong> Track who changed what, and when, to resolve conflicts precisely.</p></li><li><p><strong>Merkle Trees:</strong> Group and summarize records as hashes; compare just summaries first to find divergences efficiently.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XX_B!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00eedf76-b399-4320-a147-c3725cae63a3_720x480.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XX_B!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00eedf76-b399-4320-a147-c3725cae63a3_720x480.heic 424w, https://substackcdn.com/image/fetch/$s_!XX_B!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00eedf76-b399-4320-a147-c3725cae63a3_720x480.heic 848w, https://substackcdn.com/image/fetch/$s_!XX_B!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00eedf76-b399-4320-a147-c3725cae63a3_720x480.heic 1272w, https://substackcdn.com/image/fetch/$s_!XX_B!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00eedf76-b399-4320-a147-c3725cae63a3_720x480.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XX_B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00eedf76-b399-4320-a147-c3725cae63a3_720x480.heic" width="720" height="480" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/00eedf76-b399-4320-a147-c3725cae63a3_720x480.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:480,&quot;width&quot;:720,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31475,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/178496085?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00eedf76-b399-4320-a147-c3725cae63a3_720x480.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XX_B!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00eedf76-b399-4320-a147-c3725cae63a3_720x480.heic 424w, https://substackcdn.com/image/fetch/$s_!XX_B!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00eedf76-b399-4320-a147-c3725cae63a3_720x480.heic 848w, https://substackcdn.com/image/fetch/$s_!XX_B!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00eedf76-b399-4320-a147-c3725cae63a3_720x480.heic 1272w, https://substackcdn.com/image/fetch/$s_!XX_B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00eedf76-b399-4320-a147-c3725cae63a3_720x480.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>How Merkle Trees Empower Anti-Entropy in Real Systems</strong></h2><p>This is exactly the strategy used by distributed databases like Cassandra and DynamoDB. Each node in the system summarizes its stored data using a Merkle tree. When it&#8217;s time to reconcile &#8212; maybe after a network glitch or data loss &#8212; nodes quickly compare root hashes, then only dive deeper where discrepancies are found. This process scales effortlessly, allowing massive companies (and databases) to keep their records in harmony with minimal effort.</p><p>So, instead of drowning in a sea of spreadsheets, your accounts team uses anti-entropy protocols &#8212; and especially Merkle trees &#8212; to work smarter, not harder, keeping the entire global ledger consistent, up-to-date, and audit-ready.</p><p><strong>In essence:</strong><br>Anti-entropy, led by the power of Merkle trees, turns the messy challenge of financial reconciliation across a giant organization into a fast, targeted process &#8212; ensuring global consistency with a fraction of the work. This is the same magic powering reliable, scale-proof distributed databases today.</p><h2><strong>Implementing a Merkle Tree in Code</strong></h2><p>To truly appreciate the power of Merkle trees in distributed systems, let&#8217;s walk through a practical implementation in Go. This example captures the fundamental operations: building the tree, generating cryptographic hashes, and efficiently identifying differences between trees.</p><h3><strong>Building the Merkle Tree</strong></h3><p>At the core, a Merkle tree organizes data blocks into a hierarchical hash structure. Our implementation breaks down like this:</p><ul><li><p>We start by dividing data into buckets (or partitions), each containing a fixed number of keys.</p></li><li><p>Each bucket&#8217;s combined data is hashed, creating a leaf node.</p></li><li><p>We then recursively pair leaf nodes, concatenate their hashes, and hash again to form parent nodes.</p></li><li><p>This process repeats until a single root node remains, representing a compact summary of the entire dataset.</p></li></ul><p>Here&#8217;s how these pieces fit together in code:</p><pre><code>// MerkleNode represents a leaf or internal node with its hash and children.
type MerkleNode struct {
    Hash     string
    Left     *MerkleNode
    Right    *MerkleNode
    KeyRange []string  // Only non-empty for leaves, marks keys covered
}

// Tree manages the Merkle tree for a dataset or node partition.
type Tree struct {
    BucketSize int       // Number of keys per leaf bucket
    Root       *MerkleNode
    mu         sync.RWMutex  // Protects concurrent access
}</code></pre><p>When you call the <code>Build</code> method with sorted keys and their corresponding data, it hashes data bucket by bucket to create leaves:</p><pre><code>func (t *Tree) buildLeaves(keys []string, kvs map[string][]byte) []*MerkleNode {
    var leaves []*MerkleNode
    for i := 0; i &lt; len(keys); i += t.BucketSize {
        end := min(i + t.BucketSize, len(keys))
        bucket := keys[i:end]
        
        // Concatenate keys and their values (hex encoded)
        data := &#8220;&#8221;
        for _, k := range bucket {
            data += k + &#8220;:&#8221; + hex.EncodeToString(kvs[k])
        }
        h := sha256.Sum256([]byte(data))
        
        leaves = append(leaves, &amp;MerkleNode{
            Hash: hex.EncodeToString(h[:]),
            KeyRange: bucket,
        })
    }
    return leaves
}</code></pre><p>The <code>buildMerkle</code> function then recursively builds parents by hashing pairs of child hashes, gracefully handling odd numbers of nodes:</p><pre><code>func buildMerkle(nodes []*MerkleNode) *MerkleNode {
    if len(nodes) == 0 {
        return nil
    }
    if len(nodes) == 1 {
        return nodes[0]
    }
    var parents []*MerkleNode
    for i := 0; i &lt; len(nodes); i += 2 {
        if i+1 &lt; len(nodes) {
            data := nodes[i].Hash + nodes[i+1].Hash
            h := sha256.Sum256([]byte(data))
            parents = append(parents, &amp;MerkleNode{
                Hash: hex.EncodeToString(h[:]),
                Left: nodes[i],
                Right: nodes[i+1],
            })
        } else {
            parents = append(parents, nodes[i])
        }
    }
    return buildMerkle(parents)
}</code></pre><p>After building, the single root hash summarizes the whole dataset:</p><pre><code>func (t *Tree) RootHash() string {
    t.mu.RLock()
    defer t.mu.RUnlock()
    if t.Root == nil {
        return &#8220;&#8221;
    }
    return t.Root.Hash
}</code></pre><h3><strong>Comparing Trees to Identify Differences</strong></h3><p>One of the biggest benefits of Merkle trees is their ability to efficiently detect exactly where two datasets differ &#8212; without scanning everything. This is done by recursively comparing node hashes from the roots downward:</p><pre><code>// Diff returns key ranges that differ between two Merkle trees.
func (t *Tree) Diff(other *Tree) ([][]string, error) {
    t.mu.RLock()
    defer t.mu.RUnlock()
    if t.Root == nil || other.Root == nil {
        return nil, errors.New(&#8221;cannot diff: tree(s) not built&#8221;)
    }
    diffs := make([][]string, 0)
    diffHelper(t.Root, other.Root, &amp;diffs)
    return diffs, nil
}

// Helper to recursively collect differing leaf key ranges.
func diffHelper(a, b *MerkleNode, diffs *[][]string) {
    if a == nil || b == nil {
        return
    }
    if a.Hash == b.Hash {
        return // subtree matches; no difference
    }
    if a.Left == nil &amp;&amp; a.Right == nil &amp;&amp; b.Left == nil &amp;&amp; b.Right == nil {
        // Both leaves differ; record key range
        *diffs = append(*diffs, a.KeyRange)
        return
    }
    if a.Left != nil &amp;&amp; b.Left != nil {
        diffHelper(a.Left, b.Left, diffs)
    }
    if a.Right != nil &amp;&amp; b.Right != nil {
        diffHelper(a.Right, b.Right, diffs)
    }
}</code></pre><p>This targeted approach means two large datasets can reconcile efficiently by syncing only the data within the mismatched key ranges.</p><h3><strong>Summary</strong></h3><p>This Go implementation demonstrates how Merkle trees:</p><ul><li><p>Build a cryptographic hash tree from raw data in buckets to leaves to root.</p></li><li><p>Provide a single summary root hash for efficient data verification.</p></li><li><p>Enable fast difference detection by recursive hash comparison, isolating only exact data shards that need syncing.</p></li></ul><p>Understanding and implementing these core operations is the first step toward applying Merkle trees for secure, scalable data consistency in real distributed systems.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KL2d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa507eb3-1ccd-4f0b-a954-6b05803ff9b5_720x480.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KL2d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa507eb3-1ccd-4f0b-a954-6b05803ff9b5_720x480.heic 424w, https://substackcdn.com/image/fetch/$s_!KL2d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa507eb3-1ccd-4f0b-a954-6b05803ff9b5_720x480.heic 848w, https://substackcdn.com/image/fetch/$s_!KL2d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa507eb3-1ccd-4f0b-a954-6b05803ff9b5_720x480.heic 1272w, https://substackcdn.com/image/fetch/$s_!KL2d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa507eb3-1ccd-4f0b-a954-6b05803ff9b5_720x480.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KL2d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa507eb3-1ccd-4f0b-a954-6b05803ff9b5_720x480.heic" width="720" height="480" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aa507eb3-1ccd-4f0b-a954-6b05803ff9b5_720x480.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:480,&quot;width&quot;:720,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24559,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/178496085?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa507eb3-1ccd-4f0b-a954-6b05803ff9b5_720x480.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!KL2d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa507eb3-1ccd-4f0b-a954-6b05803ff9b5_720x480.heic 424w, https://substackcdn.com/image/fetch/$s_!KL2d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa507eb3-1ccd-4f0b-a954-6b05803ff9b5_720x480.heic 848w, https://substackcdn.com/image/fetch/$s_!KL2d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa507eb3-1ccd-4f0b-a954-6b05803ff9b5_720x480.heic 1272w, https://substackcdn.com/image/fetch/$s_!KL2d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa507eb3-1ccd-4f0b-a954-6b05803ff9b5_720x480.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Using Merkle Trees for Anti-Entropy: Step-by-Step</strong></h2><ol><li><p><strong>Each node builds its Merkle tree </strong>over its dataset.</p></li><li><p><strong>Nodes exchange root hashes.</strong> If equal, they&#8217;re in sync; if not, mismatches exist.</p></li><li><p><strong>Compare children hashes recursively</strong> until you reach mismatched leaves.</p></li><li><p><strong>Sync only the data for mismatched leaves,</strong> not the entire dataset.</p></li><li><p><strong>Rebuild trees and repeat as needed.</strong> This gives you fast, granular repair.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qqAG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46715b6-269b-4baf-a82e-dbb209de8119_720x480.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qqAG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46715b6-269b-4baf-a82e-dbb209de8119_720x480.heic 424w, https://substackcdn.com/image/fetch/$s_!qqAG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46715b6-269b-4baf-a82e-dbb209de8119_720x480.heic 848w, https://substackcdn.com/image/fetch/$s_!qqAG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46715b6-269b-4baf-a82e-dbb209de8119_720x480.heic 1272w, https://substackcdn.com/image/fetch/$s_!qqAG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46715b6-269b-4baf-a82e-dbb209de8119_720x480.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qqAG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46715b6-269b-4baf-a82e-dbb209de8119_720x480.heic" width="720" height="480" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d46715b6-269b-4baf-a82e-dbb209de8119_720x480.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:480,&quot;width&quot;:720,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:22016,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/178496085?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46715b6-269b-4baf-a82e-dbb209de8119_720x480.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qqAG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46715b6-269b-4baf-a82e-dbb209de8119_720x480.heic 424w, https://substackcdn.com/image/fetch/$s_!qqAG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46715b6-269b-4baf-a82e-dbb209de8119_720x480.heic 848w, https://substackcdn.com/image/fetch/$s_!qqAG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46715b6-269b-4baf-a82e-dbb209de8119_720x480.heic 1272w, https://substackcdn.com/image/fetch/$s_!qqAG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd46715b6-269b-4baf-a82e-dbb209de8119_720x480.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Performance Considerations</strong></h2><h3><strong>Efficiency:</strong></h3><ul><li><p>Merkle trees transform O(n)<em>O</em>(<em>n</em>) comparisons into O(log&#8289;n)<em>O</em>(log<em>n</em>), minimizing bandwidth and compute, critical at scale.</p></li></ul><h3><strong>Trade-offs:</strong></h3><ul><li><p>Depth &amp; Bucket Size: Small buckets yield deep trees and fine granularity for repairs; large buckets are faster to build but coarser.</p></li><li><p>Branching Factor: Binary is simple, but higher factors further reduce tree height.</p></li><li><p>Hash Function: Use cryptographic hashes for security, but know they&#8217;re slower than simple checksums.</p></li></ul><h3><strong>Best Practices:</strong></h3><ul><li><p>Keep keys sorted and buckets consistent.</p></li><li><p>Regularly audit cryptographic choice and correctness.</p></li><li><p>Avoid over-deep trees or inconsistent bucketing, as these kill performance.</p></li></ul><h2><strong>Case Study: Cassandra&#8217;s Anti-Entropy Repair</strong></h2><p>Scenario: A network partition causes one node to lag. When it rejoins, Cassandra must reconcile data &#8212; efficiently.</p><p><strong>Process:</strong></p><ul><li><p>Each node builds a Merkle tree for a partition.</p></li><li><p>Nodes exchange Merkle roots; if mismatched, they compare subtrees.</p></li><li><p>Drill down recursively until mismatching leaves found; sync only those ranges.</p></li></ul><p><strong>Visual Flow:</strong></p><ol><li><p>Build trees</p></li><li><p>Exchange and compare roots</p></li><li><p>Drill to mismatched children</p></li><li><p>Sync only affected ranges</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!51lk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e637896-572e-44d0-9bb3-ddc9af1d3b55_720x480.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!51lk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e637896-572e-44d0-9bb3-ddc9af1d3b55_720x480.heic 424w, https://substackcdn.com/image/fetch/$s_!51lk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e637896-572e-44d0-9bb3-ddc9af1d3b55_720x480.heic 848w, https://substackcdn.com/image/fetch/$s_!51lk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e637896-572e-44d0-9bb3-ddc9af1d3b55_720x480.heic 1272w, https://substackcdn.com/image/fetch/$s_!51lk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e637896-572e-44d0-9bb3-ddc9af1d3b55_720x480.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!51lk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e637896-572e-44d0-9bb3-ddc9af1d3b55_720x480.heic" width="720" height="480" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4e637896-572e-44d0-9bb3-ddc9af1d3b55_720x480.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:480,&quot;width&quot;:720,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:37270,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/178496085?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e637896-572e-44d0-9bb3-ddc9af1d3b55_720x480.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!51lk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e637896-572e-44d0-9bb3-ddc9af1d3b55_720x480.heic 424w, https://substackcdn.com/image/fetch/$s_!51lk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e637896-572e-44d0-9bb3-ddc9af1d3b55_720x480.heic 848w, https://substackcdn.com/image/fetch/$s_!51lk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e637896-572e-44d0-9bb3-ddc9af1d3b55_720x480.heic 1272w, https://substackcdn.com/image/fetch/$s_!51lk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e637896-572e-44d0-9bb3-ddc9af1d3b55_720x480.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Conclusion</strong></h2><p>Merkle trees are the backbone of fast, scalable, and resilient data synchronization in distributed systems. They enable efficient, targeted anti-entropy repairs, keeping massive databases like Cassandra and DynamoDB consistent with minimal overhead &#8212; even in the face of network partitions or data loss.</p><p>As data volumes grow and systems span continents, intelligent anti-entropy protocols will only become more crucial. The future? Expect even tighter cryptographic guarantees, hybrid data structures, and smarter reconciliation engines &#8212; keeping the world&#8217;s distributed data trustworthy and lightning-fast.</p><h2><strong>Stay Connected!</strong></h2><ul><li><p>&#128161; Follow me on LinkedIn: <a href="https://www.linkedin.com/in/architagarwal984/">Archit Agarwal</a></p></li><li><p>&#127909; Subscribe to my YouTube: <a href="https://www.youtube.com/c/TheExceptionHandler">The Exception Handler</a></p></li><li><p>&#128236; Sign up for my newsletter: <a href="https://www.linkedin.com/newsletters/the-weekly-golang-journal-7261403856079597568/">The Weekly Golang Journal</a></p></li><li><p>&#9997;&#65039; Follow me on Medium: <a href="https://medium.com/@architagr">@architagr</a></p></li><li><p>&#128104;&#8205;&#128187; Join my subreddit: <a href="https://www.reddit.com/r/GolangJournal/">r/GolangJournal</a></p></li><li><p>&#128161; Follow me on Twitter: <a href="https://x.com/architagr">@architagr</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Security Practices and User Management]]></title><description><![CDATA[The complete "Chapter 15: Security Practices and User Management" from the book, GitHub Foundations Certification Guide (Packt, 2025), by Ayodeji Ayodele]]></description><link>https://deepengineering.substack.com/p/security-practices-and-user-management</link><guid isPermaLink="false">https://deepengineering.substack.com/p/security-practices-and-user-management</guid><dc:creator><![CDATA[Ayodeji Ayodele]]></dc:creator><pubDate>Thu, 06 Nov 2025 08:39:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!c1C4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d177e6d-10ef-4e8d-9954-776efd177ad7_2250x2775" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to <em>Chapter 15</em>, where we explore the essentials of GitHub security and user management! By now, you&#8217;re familiar with the essentials, you have collaborated effectively, and you&#8217;ve started leveraging GitHub for your career. Now, it&#8217;s time to focus on protecting your work. In this chapter, we&#8217;ll explore GitHub&#8217;s built-in security features &#8211; such as setting up two-factor authentication, managing user permissions, and securing your CI/CD pipelines. These practices are critical for maintaining repository integrity and will also help you prepare for the certification exam.</p><p>We will cover the following main topics:</p><ul><li><p>GitHub security features</p></li><li><p>Managing access and permissions</p></li><li><p>Best practices for repository security</p></li></ul><div><hr></div><h1>GitHub security features</h1><p>In this section, we&#8217;ll explore the various security features GitHub offers to help you protect your repositories and manage user access effectively.</p><h2>Two-Factor Authentication (2FA)</h2><p>Two-factor authentication adds an extra layer of security to your GitHub account beyond just your password. By requiring a second form of verification, it ensures that even if someone gets hold of your password, they won&#8217;t be able to access your account without the second factor.</p><p>2FA is crucial for securing your account for enhanced security, mitigating against credential theft, protecting against phishing, and complying with security standards in many organizations and projects. Which 2FA methods are configurable on GitHub?</p><h3>Available 2FA methods</h3><p>GitHub offers several methods for enabling <strong>Two-Factor Authentication</strong> (<strong>2FA</strong>) to enhance the security of your account. Here are the available 2FA methods:</p><ul><li><p><strong>Time-Based One-Time Password (TOTP) authenticator apps</strong>:</p><ul><li><p>Use apps such as Google Authenticator, Authy, or Microsoft Authenticator to generate a time-based code</p></li><li><p>Recommended for its reliability and security</p></li></ul></li><li><p><strong>Short Message Service (SMS)</strong>:</p><ul><li><p>Receive a verification code via text message</p></li><li><p>Less secure compared to TOTP apps, but still an option</p></li></ul></li><li><p><strong>Physical security keys</strong>:</p><ul><li><p>Use hardware devices such as <strong>YubiKeys</strong> that support FIDO U2F or WebAuthn standards</p></li><li><p>Provide a high level of security by requiring physical possession of the key</p></li></ul></li><li><p><strong>Virtual security keys</strong>:</p><ul><li><p>Utilize built-in security features of personal devices, such as Windows Hello, Face ID, or Touch ID</p></li><li><p>Convenient and secure, leveraging device-specific authentication</p></li></ul></li><li><p><strong>GitHub Mobile</strong>:</p><ul><li><p>Use the GitHub Mobile app to authenticate using public-key cryptography</p></li><li><p>Does not rely on TOTP and provides a seamless experience</p></li></ul></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VrfZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd73d83a-b864-4607-aa88-59bce75f605a_824x543.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VrfZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd73d83a-b864-4607-aa88-59bce75f605a_824x543.png 424w, https://substackcdn.com/image/fetch/$s_!VrfZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd73d83a-b864-4607-aa88-59bce75f605a_824x543.png 848w, https://substackcdn.com/image/fetch/$s_!VrfZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd73d83a-b864-4607-aa88-59bce75f605a_824x543.png 1272w, https://substackcdn.com/image/fetch/$s_!VrfZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd73d83a-b864-4607-aa88-59bce75f605a_824x543.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VrfZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd73d83a-b864-4607-aa88-59bce75f605a_824x543.png" width="824" height="543" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cd73d83a-b864-4607-aa88-59bce75f605a_824x543.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:543,&quot;width&quot;:824,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 15.1: Available 2FA methods on GitHub&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 15.1: Available 2FA methods on GitHub" title="Figure 15.1: Available 2FA methods on GitHub" srcset="https://substackcdn.com/image/fetch/$s_!VrfZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd73d83a-b864-4607-aa88-59bce75f605a_824x543.png 424w, https://substackcdn.com/image/fetch/$s_!VrfZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd73d83a-b864-4607-aa88-59bce75f605a_824x543.png 848w, https://substackcdn.com/image/fetch/$s_!VrfZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd73d83a-b864-4607-aa88-59bce75f605a_824x543.png 1272w, https://substackcdn.com/image/fetch/$s_!VrfZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd73d83a-b864-4607-aa88-59bce75f605a_824x543.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 15.1: Available 2FA methods on GitHub</figcaption></figure></div><p>These methods offer flexibility and varying levels of security, allowing you to choose the one that best fits your needs. For the highest security, it&#8217;s recommended to use TOTP apps or physical security keys. Let&#8217;s take a look at how to set this up.</p><blockquote><p><strong>Certification tip</strong></p><p>The GitHub Foundations exam often includes questions on 2FA setup and best practices. Make sure you understand both how to enable 2FA and how to use recovery methods.</p></blockquote><h3>Setting up 2FA on GitHub</h3><p>To add an extra layer of security to your GitHub account, enable two-factor authentication (2FA) by following these steps:</p><ol><li><p>Go to your GitHub individual account settings by clicking on your <em>avatar</em> in the top-right-hand corner and clicking on <strong>Settings</strong>.</p></li><li><p>In the left sidebar, under <strong>Access</strong>, click on <strong>Password and authentication</strong>.</p></li><li><p>If not enabled yet, select <strong>Enable two-factor authentication</strong>.</p></li><li><p>Under <strong>Two-factor authentication</strong>, click <strong>Add </strong>next to the 2FA method of choice.</p></li><li><p>Follow the prompts to set up 2FA using the method selected.</p></li></ol><p>Optionally, you could set your preferred 2FA method if you enrolled in more than one.</p><p>That&#8217;s it! You&#8217;re all set up.</p><p>You will also notice <strong>Recovery codes</strong> under the <strong>Recovery options</strong> section (this section will appear only if the 2FA method is set).</p><p>Recovery codes are essential for regaining access to your GitHub account if you lose access to your 2FA credentials. These codes act as a backup method, allowing you to log in even if you can&#8217;t use your primary 2FA method, such as an authentication app or SMS. When you enable 2FA, GitHub provides a set of recovery codes that you should store securely, such as in a password manager or a safe place. If you ever lose access to your 2FA device, you can use one of these recovery codes to regain entry to your account, ensuring you are not permanently locked out.</p><p>GitHub gives you 8 recovery codes. Store them securely. You can regenerate these if needed, but old ones will be invalidated.</p><h2>Branch protection rules</h2><p>We discussed branch protection rules extensively in <em>Chapter 5</em>, <em>Branching and Merging Strategies</em>. Be sure to read this in preparation for your exam.</p><p>Branch protection rules help you enforce certain workflows and requirements before changes can be merged into your protected branches. This ensures that your codebase remains stable and secure. You can configure branch protection rules and, among many other measures, enforce code reviews, ensuring that all changes are reviewed and approved before they are merged.</p><blockquote><p><strong>Certification tip</strong></p><p>You&#8217;ll need to know how to configure branch protection rules and enable Dependabot alerts for the exam.</p></blockquote><h2>Security configurations</h2><p>GitHub provides various security settings as a collection that you can configure to enhance the security of the repositories in your organization. You can create a customized security configuration from scratch or choose the GitHub-recommended configuration that already comes preset with its settings.</p><p>GitHub-recommended security configurations are predefined settings that follow best practices to enhance security, such as enabling Dependabot alerts and secret scanning by default. Custom configurations, on the other hand, allow you to tailor security settings to meet specific needs or requirements of your project or organization, providing flexibility to adjust features such as branch protection rules and access controls.</p><p>To manage security settings at the <strong>organization level</strong>:</p><ol><li><p>Go to your organization&#8217;s main page on GitHub (remember that this is an organization, not a repo). <em>For more information on how to create an organization, review Lab 2.1 in <a href="https://subscription.packtpub.com/book/cloud-and-networking/9781836206057/20">Chapter 2</a>, Navigating the GitHub Interface</em>.</p></li><li><p>Click on <strong>Settings</strong>.</p></li><li><p>In the left sidebar, under <strong>Security</strong>, click <strong>Advanced Security</strong> to expand.</p></li><li><p>Then click on <strong>Configurations</strong>.</p></li><li><p>Choose to edit the GitHub-recommended security configuration by clicking on the edit (</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!K4VO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf293b77-2890-42f6-b67f-ebbed0088a38_25x27.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!K4VO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf293b77-2890-42f6-b67f-ebbed0088a38_25x27.png 424w, https://substackcdn.com/image/fetch/$s_!K4VO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf293b77-2890-42f6-b67f-ebbed0088a38_25x27.png 848w, https://substackcdn.com/image/fetch/$s_!K4VO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf293b77-2890-42f6-b67f-ebbed0088a38_25x27.png 1272w, https://substackcdn.com/image/fetch/$s_!K4VO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf293b77-2890-42f6-b67f-ebbed0088a38_25x27.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!K4VO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf293b77-2890-42f6-b67f-ebbed0088a38_25x27.png" width="25" height="27" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/df293b77-2890-42f6-b67f-ebbed0088a38_25x27.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:27,&quot;width&quot;:25,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!K4VO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf293b77-2890-42f6-b67f-ebbed0088a38_25x27.png 424w, https://substackcdn.com/image/fetch/$s_!K4VO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf293b77-2890-42f6-b67f-ebbed0088a38_25x27.png 848w, https://substackcdn.com/image/fetch/$s_!K4VO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf293b77-2890-42f6-b67f-ebbed0088a38_25x27.png 1272w, https://substackcdn.com/image/fetch/$s_!K4VO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf293b77-2890-42f6-b67f-ebbed0088a38_25x27.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>) icon. Alternatively, you can click on <strong>New configuration</strong> to customize a new one.</p></li><li><p>Configure the security settings as needed.</p></li></ol><p>To manage <strong>repository-specific</strong> security settings:</p><ol><li><p>Navigate to the repository&#8217;s main page on GitHub.</p></li><li><p>Click on <strong>Settings</strong>.</p></li><li><p>In the left sidebar, under <strong>Security</strong>, click <strong>Advanced Security</strong>.</p></li><li><p>Enable or configure security features such as <strong>Dependabot alerts, </strong>secret scanning, and code scanning. The latter two may be missing from your view if the repository is private or internal.</p><blockquote><p>Secret scanning and code scanning are GitHub Advanced Security features and are only available as a paid subscription for private or internal repositories, or free if your repository is open source, that is, visibility is <strong>Public</strong>.</p></blockquote></li><li><p>Optionally, if you wish to configure secret scanning or code scanning, click on <strong>General</strong> from the left sidebar and scroll down to <strong>Danger Zone</strong>. Next to <strong>Change repository visibility</strong>, click on <strong>Change visibility</strong> and select <strong>Change to public</strong>. Be sure to follow the instructions.</p></li></ol><blockquote><p><strong>Certification tip</strong></p><p>Questions may come up regarding changing the visibility of a repository from public to private, or vice versa. Be sure to understand the implications of making a repo public. Read more about public repositories in <em><a href="https://subscription.packtpub.com/book/cloud-and-networking/9781836206057/20">Chapter 2</a>, Navigating the GitHub Interface</em> and <em><a href="https://subscription.packtpub.com/book/cloud-and-networking/9781836206057/11">Chapter 11</a>, Contributing to Open Source Projects</em>.</p></blockquote><p>Dependabot, secret scanning, and code scanning are all examples of security features that GitHub offers. Some of these features require a paid subscription, some are free only for public repositories, while others are completely free out of the box.</p><h2>What is Dependabot?</h2><p>Dependabot is a feature on GitHub that helps keep your project&#8217;s dependencies up to date automatically. It works by regularly checking your project&#8217;s dependency files (such as <code>package.json</code>, <code>requirements.txt</code>, etc.) for outdated packages and then creating pull requests to update them to the latest versions.</p><p>Key features of Dependabot include the following:</p><ul><li><p><strong>Automated dependency updates</strong>: It scans your project and creates pull requests to update dependencies</p></li><li><p><strong>Security alerts</strong>: It integrates with GitHub&#8217;s security features to alert you about vulnerabilities in your dependencies and can automatically fix them</p></li><li><p><strong>Customizable configuration</strong>: You can configure how often it checks for updates, which dependencies to ignore, and more, using a <code>dependabot.yml</code> file</p></li><li><p><strong>Supports multiple languages</strong>: Works with JavaScript, Python, Ruby, Java, PHP, and more</p></li></ul><p>Here is an example flow:</p><ol><li><p>You enable Dependabot in your GitHub repository.</p></li><li><p>It checks for outdated or vulnerable dependencies.</p></li><li><p>It creates a pull request with the updated version.</p></li><li><p>You review and merge the pull request.</p></li></ol><p>Now let&#8217;s talk about managing alerts and vulnerabilities.</p><h2>Security alerts and vulnerability management</h2><p>GitHub helps you stay on top of potential security issues with automated alerts and tools to manage vulnerabilities. Let&#8217;s examine some of these:</p><ul><li><p><strong>Dependabot alerts and security updates</strong>:</p><ul><li><p><strong>Dependabot alerts</strong>: Automatically scans your dependencies for known vulnerabilities and notifies you if any are found</p></li><li><p><strong>Dependabot security updates</strong>: Automatically generates pull requests to update vulnerable dependencies to secure versions</p></li></ul></li></ul><p>How can you enable and manage Dependabot alerts?</p><ol><li><p>Go to the repository&#8217;s main page on GitHub.</p></li><li><p>Click on <strong>Settings</strong>.</p></li><li><p>In the left sidebar, under <strong>Security</strong>, click <strong>Advanced Security</strong>.</p></li><li><p>Under <strong>Dependabot alerts</strong>, click <strong>Enable</strong> if not already enabled.</p></li><li><p>Afterward, you can review and manage alerts from the <strong>Security</strong> tab of the repository.</p></li></ol><ul><li><p><strong>Code scanning</strong>:</p><ul><li><p><strong>CodeQL</strong>: A powerful code analysis engine that scans your code for security vulnerabilities and coding errors. It integrates with GitHub Actions to run scans on every push or pull request.</p></li><li><p><strong>Autofix</strong>: Uses AI to suggest fixes for detected vulnerabilities, streamlining the remediation process.</p></li></ul></li><li><p><strong>Secret scanning</strong>: Detects and alerts you if sensitive information, such as API keys or passwords, is accidentally committed to your repository. This helps prevent unauthorized access and potential security breaches.</p></li><li><p><strong>Security overview dashboard</strong>: Provides a centralized view of your security alerts and vulnerabilities across all repositories. This dashboard helps you prioritize and manage security issues more effectively.</p></li><li><p><strong>Vulnerability management integrations</strong>: Integrates with third-party vulnerability management tools to consolidate and prioritize vulnerabilities, automate risk mitigation, and visualize alerts within your existing security posture.</p></li></ul><p>Talking about third-party integrations, GitHub supports receiving <strong>Static Analysis Results Interchange Format</strong> (<strong>SARIF</strong>) reports from various third-party security tools. Some of the commonly used tools include the following:</p><ul><li><p><strong>ESLint</strong>: A popular tool for identifying and reporting on patterns found in ECMAScript/JavaScript code</p></li><li><p><strong>Bandit</strong>: A tool designed to find common security issues in Python code</p></li><li><p><strong>Brakeman</strong>: A static analysis tool that checks Ruby on Rails applications for security vulnerabilities</p></li><li><p><strong>Checkmarx</strong>: A comprehensive <strong>Static Application Security Testing</strong> (<strong>SAST</strong>) tool</p></li><li><p><strong>Fortify</strong>: A suite of tools for static and dynamic application security testing</p></li><li><p><strong>SonarQube</strong>: An open source platform for continuous inspection of code quality</p></li><li><p><strong>Veracode</strong>: A cloud-based service for static and dynamic application security testing</p></li></ul><p>These tools generate SARIF files that can be uploaded to GitHub, allowing you to view and manage security alerts directly within your repository.</p><p>To handle security advisories and alerts, navigate to the <strong>Security</strong> tab of the repository (<em>you will find this tab on both the organization and the repository levels</em>). Examine the difference between the <strong>Security</strong> tabs of both levels. You will notice a stark difference in what you see. This is because the security overview at the organization level rolls up all the security advisories across all its repos, whereas the scope of the repo level is limited to only vulnerability findings of that repo.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Gjr6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2eb9205-79ee-4581-9d45-02378640b9d2_825x487.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Gjr6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2eb9205-79ee-4581-9d45-02378640b9d2_825x487.png 424w, https://substackcdn.com/image/fetch/$s_!Gjr6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2eb9205-79ee-4581-9d45-02378640b9d2_825x487.png 848w, https://substackcdn.com/image/fetch/$s_!Gjr6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2eb9205-79ee-4581-9d45-02378640b9d2_825x487.png 1272w, https://substackcdn.com/image/fetch/$s_!Gjr6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2eb9205-79ee-4581-9d45-02378640b9d2_825x487.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Gjr6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2eb9205-79ee-4581-9d45-02378640b9d2_825x487.png" width="825" height="487" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2eb9205-79ee-4581-9d45-02378640b9d2_825x487.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:487,&quot;width&quot;:825,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 15.2: Security Overview at the organization level&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 15.2: Security Overview at the organization level" title="Figure 15.2: Security Overview at the organization level" srcset="https://substackcdn.com/image/fetch/$s_!Gjr6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2eb9205-79ee-4581-9d45-02378640b9d2_825x487.png 424w, https://substackcdn.com/image/fetch/$s_!Gjr6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2eb9205-79ee-4581-9d45-02378640b9d2_825x487.png 848w, https://substackcdn.com/image/fetch/$s_!Gjr6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2eb9205-79ee-4581-9d45-02378640b9d2_825x487.png 1272w, https://substackcdn.com/image/fetch/$s_!Gjr6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2eb9205-79ee-4581-9d45-02378640b9d2_825x487.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 15.2: Security Overview at the organization level</figcaption></figure></div><p>These tools and features help you maintain a robust security posture by automating the detection and management of vulnerabilities, ensuring your codebase remains secure.</p><p>In the next section, we will talk about how permissions and authorization can help in securing your code.</p><div><hr></div><h1>Managing access and permissions</h1><p>Effective management of access and permissions is crucial for maintaining the security and integrity of your GitHub repositories. This section delves into the various methods GitHub provides to control who has access to your repositories and what they can do.</p><h2>User roles and permissions</h2><p>GitHub offers a range of user roles to help you manage access and permissions effectively. Understanding these roles is key to maintaining a secure and organized workflow.</p><h3>Overview of different user roles</h3><p>User roles on GitHub come in three tiers: roles at the Enterprise level, Organization level, and Repository level. Here are the default roles at the Enterprise and Organization levels:</p><ul><li><p><strong>Owner</strong>: The owner has full administrative access to the organization and its repositories. This role can manage settings, users, and billing.</p></li><li><p><strong>Member</strong>: Members have basic access to repositories, typically for contributing code. They can create issues, submit pull requests, and review code.</p></li><li><p><strong>Billing manager</strong>: Billing managers can manage billing settings such as changing billing plans, managing payment methods, downloading and receiving receipts, or managing sponsorships.</p></li></ul><p>When inviting new collaborators to your organization for the first time, you choose one of these three.</p><blockquote><p><strong>Certification tip</strong></p><p>Be prepared to identify role-based use cases. The exam may ask you to match specific user scenarios with appropriate GitHub roles.</p></blockquote><h3>Assigning roles to users</h3><p>To manage access within your organization, you can assign specific roles to members by following these steps:</p><ol><li><p>Navigate to your organization&#8217;s main page on GitHub.</p></li><li><p>Click on the <strong>People </strong>tab in the organization&#8217;s navigation bar.</p></li><li><p>If the user doesn&#8217;t already exist in the organization, you can invite them by clicking on <strong>Invite member</strong>.</p></li><li><p>Supply the user&#8217;s GitHub handle and click on <strong>Invite</strong>.</p></li><li><p>For an existing member, locate the user you want to assign a role to and click on the ellipsis dropdown next to their name and select <strong>Change role&#8230;</strong>.</p></li><li><p>Select the appropriate role and click on <strong>Send invitation</strong> (for new invitations) or <strong>Change role</strong> (existing members).</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8fDS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e5b7f3b-110c-4891-94a3-9a3fb67455b3_759x566.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8fDS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e5b7f3b-110c-4891-94a3-9a3fb67455b3_759x566.png 424w, https://substackcdn.com/image/fetch/$s_!8fDS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e5b7f3b-110c-4891-94a3-9a3fb67455b3_759x566.png 848w, https://substackcdn.com/image/fetch/$s_!8fDS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e5b7f3b-110c-4891-94a3-9a3fb67455b3_759x566.png 1272w, https://substackcdn.com/image/fetch/$s_!8fDS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e5b7f3b-110c-4891-94a3-9a3fb67455b3_759x566.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8fDS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e5b7f3b-110c-4891-94a3-9a3fb67455b3_759x566.png" width="759" height="566" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7e5b7f3b-110c-4891-94a3-9a3fb67455b3_759x566.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:566,&quot;width&quot;:759,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 15.3: Example invitation showing the default available roles&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 15.3: Example invitation showing the default available roles" title="Figure 15.3: Example invitation showing the default available roles" srcset="https://substackcdn.com/image/fetch/$s_!8fDS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e5b7f3b-110c-4891-94a3-9a3fb67455b3_759x566.png 424w, https://substackcdn.com/image/fetch/$s_!8fDS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e5b7f3b-110c-4891-94a3-9a3fb67455b3_759x566.png 848w, https://substackcdn.com/image/fetch/$s_!8fDS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e5b7f3b-110c-4891-94a3-9a3fb67455b3_759x566.png 1272w, https://substackcdn.com/image/fetch/$s_!8fDS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e5b7f3b-110c-4891-94a3-9a3fb67455b3_759x566.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 15.3: Example invitation showing the default available roles</figcaption></figure></div><ol><li><p>Click on <strong>Change role&#8230;</strong></p></li><li><p>Select the appropriate role (<strong>Owner</strong> or <strong>Member</strong>) from the list displayed.</p></li><li><p>Click on <strong>Change role</strong>.</p></li><li><p>Alternatively, if the user only needs to be a billing manager, click on the <strong>Invite a billing manager</strong> link at the bottom of the user invitation screen (Step 3), or go to the organization&#8217;s settings and select <strong>Billing and licensing</strong> from the left navigation bar and invite them.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iva2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d06eeff-a792-405c-a413-88df6f1650cf_702x444.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iva2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d06eeff-a792-405c-a413-88df6f1650cf_702x444.png 424w, https://substackcdn.com/image/fetch/$s_!iva2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d06eeff-a792-405c-a413-88df6f1650cf_702x444.png 848w, https://substackcdn.com/image/fetch/$s_!iva2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d06eeff-a792-405c-a413-88df6f1650cf_702x444.png 1272w, https://substackcdn.com/image/fetch/$s_!iva2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d06eeff-a792-405c-a413-88df6f1650cf_702x444.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iva2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d06eeff-a792-405c-a413-88df6f1650cf_702x444.png" width="702" height="444" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6d06eeff-a792-405c-a413-88df6f1650cf_702x444.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:444,&quot;width&quot;:702,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Fig.15.4: Inviting a billing manager to GitHub&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Fig.15.4: Inviting a billing manager to GitHub" title="Fig.15.4: Inviting a billing manager to GitHub" srcset="https://substackcdn.com/image/fetch/$s_!iva2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d06eeff-a792-405c-a413-88df6f1650cf_702x444.png 424w, https://substackcdn.com/image/fetch/$s_!iva2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d06eeff-a792-405c-a413-88df6f1650cf_702x444.png 848w, https://substackcdn.com/image/fetch/$s_!iva2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d06eeff-a792-405c-a413-88df6f1650cf_702x444.png 1272w, https://substackcdn.com/image/fetch/$s_!iva2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d06eeff-a792-405c-a413-88df6f1650cf_702x444.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Fig.15.4: Inviting a billing manager to GitHub</figcaption></figure></div><p>In addition to these three, GitHub provides some more granular roles that help you define granular permissions to what a member can or cannot do at the different levels.</p><p>Here is a table of additional pre-defined roles that can be used:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6uIe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2314202-5372-402e-a996-6ed9768f1c05_1336x787.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6uIe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2314202-5372-402e-a996-6ed9768f1c05_1336x787.png 424w, https://substackcdn.com/image/fetch/$s_!6uIe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2314202-5372-402e-a996-6ed9768f1c05_1336x787.png 848w, https://substackcdn.com/image/fetch/$s_!6uIe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2314202-5372-402e-a996-6ed9768f1c05_1336x787.png 1272w, https://substackcdn.com/image/fetch/$s_!6uIe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2314202-5372-402e-a996-6ed9768f1c05_1336x787.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6uIe!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2314202-5372-402e-a996-6ed9768f1c05_1336x787.png" width="1200" height="706.8862275449102" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c2314202-5372-402e-a996-6ed9768f1c05_1336x787.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:787,&quot;width&quot;:1336,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:140407,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/178160677?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2314202-5372-402e-a996-6ed9768f1c05_1336x787.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6uIe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2314202-5372-402e-a996-6ed9768f1c05_1336x787.png 424w, https://substackcdn.com/image/fetch/$s_!6uIe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2314202-5372-402e-a996-6ed9768f1c05_1336x787.png 848w, https://substackcdn.com/image/fetch/$s_!6uIe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2314202-5372-402e-a996-6ed9768f1c05_1336x787.png 1272w, https://substackcdn.com/image/fetch/$s_!6uIe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2314202-5372-402e-a996-6ed9768f1c05_1336x787.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Table 15.1: List of predefined roles at can be used at the Organization and Repository levels</figcaption></figure></div><p>In addition to these predefined roles, you can create custom roles with a select combination of permissions if one of these predefined roles doesn&#8217;t exactly fit your needs. This can be done at the Enterprise, Organization, and Repository levels.</p><p>The custom role feature is only available on GitHub Enterprise. In addition, the custom role at the Enterprise level is only available for preview as of early 2025. Refer to GitHub Docs for the latest availability.</p><p>Now let&#8217;s move on to using teams for access control.</p><h2>Team management</h2><p>Teams allow you to group users and manage their access to repositories more efficiently. This is particularly useful for larger organizations with multiple projects. A team can either be <strong>visible</strong> or <strong>secret</strong>.</p><p>Visible teams can be seen and <code>@mentioned</code> by members of the organization, while secret teams can only be seen by their members. This will be specified during team creation. Teams can also be nested, with one team being the parent of another team.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mToD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f33ac30-8841-4452-bc68-79d9ef0cf64b_823x244.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mToD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f33ac30-8841-4452-bc68-79d9ef0cf64b_823x244.png 424w, https://substackcdn.com/image/fetch/$s_!mToD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f33ac30-8841-4452-bc68-79d9ef0cf64b_823x244.png 848w, https://substackcdn.com/image/fetch/$s_!mToD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f33ac30-8841-4452-bc68-79d9ef0cf64b_823x244.png 1272w, https://substackcdn.com/image/fetch/$s_!mToD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f33ac30-8841-4452-bc68-79d9ef0cf64b_823x244.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mToD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f33ac30-8841-4452-bc68-79d9ef0cf64b_823x244.png" width="823" height="244" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f33ac30-8841-4452-bc68-79d9ef0cf64b_823x244.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:244,&quot;width&quot;:823,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:66658,&quot;alt&quot;:&quot;Figure 15.5: A GitHub Team can be visible or secret&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 15.5: A GitHub Team can be visible or secret" title="Figure 15.5: A GitHub Team can be visible or secret" srcset="https://substackcdn.com/image/fetch/$s_!mToD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f33ac30-8841-4452-bc68-79d9ef0cf64b_823x244.png 424w, https://substackcdn.com/image/fetch/$s_!mToD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f33ac30-8841-4452-bc68-79d9ef0cf64b_823x244.png 848w, https://substackcdn.com/image/fetch/$s_!mToD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f33ac30-8841-4452-bc68-79d9ef0cf64b_823x244.png 1272w, https://substackcdn.com/image/fetch/$s_!mToD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f33ac30-8841-4452-bc68-79d9ef0cf64b_823x244.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 15.5: A GitHub Team can be visible or secret</figcaption></figure></div><p>Let&#8217;s see how we create teams.</p><h3>Creating and managing teams</h3><p>Here&#8217;s how to create a team:</p><ol><li><p>Go to your organization&#8217;s main page on GitHub.</p></li><li><p>Click on <strong>Teams</strong> in the organization&#8217;s navigation bar.</p></li><li><p>Click <strong>New team</strong> to create a new team.</p></li><li><p>Enter the team name and description, then click <strong>Create team</strong>.</p></li><li><p>Add members to the team by clicking <strong>Add a member</strong> and selecting users from the list.</p></li></ol><p>Here are some important use cases for team visibility and notifications:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wi70!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc29c97b-7915-4f6e-a783-01918149fd1e_860x382.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wi70!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc29c97b-7915-4f6e-a783-01918149fd1e_860x382.png 424w, https://substackcdn.com/image/fetch/$s_!wi70!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc29c97b-7915-4f6e-a783-01918149fd1e_860x382.png 848w, https://substackcdn.com/image/fetch/$s_!wi70!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc29c97b-7915-4f6e-a783-01918149fd1e_860x382.png 1272w, https://substackcdn.com/image/fetch/$s_!wi70!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc29c97b-7915-4f6e-a783-01918149fd1e_860x382.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wi70!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc29c97b-7915-4f6e-a783-01918149fd1e_860x382.png" width="860" height="382" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dc29c97b-7915-4f6e-a783-01918149fd1e_860x382.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:382,&quot;width&quot;:860,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:63158,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/178160677?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc29c97b-7915-4f6e-a783-01918149fd1e_860x382.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wi70!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc29c97b-7915-4f6e-a783-01918149fd1e_860x382.png 424w, https://substackcdn.com/image/fetch/$s_!wi70!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc29c97b-7915-4f6e-a783-01918149fd1e_860x382.png 848w, https://substackcdn.com/image/fetch/$s_!wi70!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc29c97b-7915-4f6e-a783-01918149fd1e_860x382.png 1272w, https://substackcdn.com/image/fetch/$s_!wi70!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc29c97b-7915-4f6e-a783-01918149fd1e_860x382.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Assigning repository access to teams</h3><p>To assign access to repositories to teams, do the following:</p><ol><li><p>Navigate to the team&#8217;s page on GitHub.</p></li><li><p>Click on the <strong>Repositories</strong> tab in the team&#8217;s navigation bar.</p></li><li><p>Click <strong>Add a repository</strong> and select the repository you want to grant access to.</p></li><li><p>Set the desired permission level (<strong>Read</strong>, <strong>Triage</strong>, <strong>Write</strong>, or others) for the team. <em>More roles and the ability to create custom roles are available if you have a paid GitHub Enterprise subscription</em>.</p></li></ol><h2>Collaborator access control</h2><p>Collaborators are individuals who are granted access to specific repositories. This is useful for managing external contributors or contractors.</p><h3>Adding collaborators to repositories:</h3><p>To give others access to a specific repository without adding them to the entire organization, you can add them as collaborators:</p><ol><li><p>Navigate to the repository&#8217;s main page on GitHub.</p></li><li><p>Click on <strong>Settings</strong> in the repository&#8217;s navigation bar.</p></li><li><p>In the left sidebar, click <strong>Collaborators and teams </strong>(org-owned repos) or<strong> Collaborators </strong>(user-owned repos).</p></li><li><p>Click <strong>Add people</strong> and start typing the username of the person you want to add.</p></li><li><p>Select their name and set the appropriate permission level (<strong>Read</strong>, <strong>Triage</strong>, <strong>Write</strong>, <strong>Maintain</strong>, <strong>Admin</strong>) for the collaborator.</p></li></ol><h3>Setting permissions for collaborators:</h3><p>Once collaborators are added, you can adjust their access levels using the following steps:</p><ol><li><p>In the <strong>Collaborators and teams</strong> section, find the collaborator you want to manage.</p></li><li><p>Click on their permission dropdown and select the desired permission level.</p></li></ol><p>One common pitfall is <strong>misconfigured roles or permissions</strong>, which can inadvertently expose sensitive code. For example, imagine a scenario where a developer creates a private repository for an internal tool but mistakenly assigns a <strong>Read</strong> role to an external contractor at the organization level. Because the repository inherits permissions from the organization, the contractor now has unintended access to the private repository. This kind of oversight can lead to accidental data leaks or unauthorized code access.</p><p>To prevent such incidents, always follow the principle of least privilege, regularly audit repository access, and use fine-grained personal access tokens for automation and integrations.</p><p>Another great feature to manage access and permissions is tokens. Two examples of tokens on GitHub are OAuth and <strong>Personal Access Tokens</strong> (<strong>PAT</strong>).</p><h2>OAuth and personal access tokens</h2><p>OAuth and personal access tokens provide secure ways to authenticate and authorize access to your GitHub account and repositories. These methods are essential for integrating third-party applications and services, that is, when you are not using an interactive login of a person. Here&#8217;s where you&#8217;ll find each of these on GitHub:</p><h3>Managing OAuth applications</h3><p>To review and manage third-party applications connected to your GitHub account, follow these steps:</p><ol><li><p>Go to your GitHub account settings.</p></li><li><p>In the left sidebar, click <strong>Developer settings</strong>.</p></li><li><p>Click <strong>OAuth Apps</strong> to view and manage your OAuth applications.</p></li><li><p>Review the list of authorized applications and revoke access if necessary.</p></li></ol><h3>Creating and using personal access tokens</h3><p>To authenticate non-interactive scripts or services, you can generate a personal access token as shown below:</p><ol><li><p>Go to your GitHub account settings.</p></li><li><p>In the left sidebar, click <strong>Developer settings</strong>.</p></li><li><p>Click <strong>Personal access tokens</strong>.</p></li><li><p>Select either <strong>Fine-grained tokens</strong> or <strong>Tokens (classic)</strong> in the submenu.</p></li><li><p>Click <strong>Generate new token </strong>(for fine-grained tokens) or select <strong>Token (classic)</strong> again for the classic token option.</p></li><li><p>Select the scopes or permissions you want to grant this token, such as repo, <code>admin:org</code>, or user.</p></li><li><p>Click <strong>Generate token</strong> and copy the token for use in your applications. Store it securely, as it will not be displayed again.</p></li></ol><p>You would notice by now that there are two types of personal access tokens on GitHub: <strong>classic</strong> and <strong>fine-grained</strong>.</p><p>Both types co-exist, with classic being the older. It is expected that GitHub will deprecate classic PAT in favour of fine-grained PAT in the future, but both of them can be used interchangeably today. Let&#8217;s quickly enumerate the differences between the two.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ro1-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2af690-a05b-43e5-9b50-ec6bc977d1f6_1316x721.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ro1-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2af690-a05b-43e5-9b50-ec6bc977d1f6_1316x721.png 424w, https://substackcdn.com/image/fetch/$s_!Ro1-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2af690-a05b-43e5-9b50-ec6bc977d1f6_1316x721.png 848w, https://substackcdn.com/image/fetch/$s_!Ro1-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2af690-a05b-43e5-9b50-ec6bc977d1f6_1316x721.png 1272w, https://substackcdn.com/image/fetch/$s_!Ro1-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2af690-a05b-43e5-9b50-ec6bc977d1f6_1316x721.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ro1-!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2af690-a05b-43e5-9b50-ec6bc977d1f6_1316x721.png" width="1200" height="657.4468085106383" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0f2af690-a05b-43e5-9b50-ec6bc977d1f6_1316x721.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:721,&quot;width&quot;:1316,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:139621,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/178160677?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2af690-a05b-43e5-9b50-ec6bc977d1f6_1316x721.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ro1-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2af690-a05b-43e5-9b50-ec6bc977d1f6_1316x721.png 424w, https://substackcdn.com/image/fetch/$s_!Ro1-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2af690-a05b-43e5-9b50-ec6bc977d1f6_1316x721.png 848w, https://substackcdn.com/image/fetch/$s_!Ro1-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2af690-a05b-43e5-9b50-ec6bc977d1f6_1316x721.png 1272w, https://substackcdn.com/image/fetch/$s_!Ro1-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2af690-a05b-43e5-9b50-ec6bc977d1f6_1316x721.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In summary, GitHub offers a range of measures to manage access and grant permissions to users and third-party applications and services. It is important to know which ones to combine to ensure the security of your code. Up next, let&#8217;s consider some good security practices for repo security.</p><div><hr></div><h1>Best practices for repository security</h1><p>Ensuring the security of your repositories is paramount to protecting your code and maintaining the integrity of your projects. In this section, we&#8217;ll cover best practices for securing your repositories on GitHub.</p><h2>Code scanning with Static Application Security Testing (SAST) tools</h2><p><strong>Static Application Security Testing</strong> (<strong>SAST</strong>) tools help identify security vulnerabilities in your codebase before they become issues in production. GitHub has a code scanning product. It is sold separately but can be integrated natively into your repos and organizations. It&#8217;s labelled <strong>GitHub Advanced Security</strong> (<strong>GHAS</strong>). GHAS is a <em>paid</em> subscription product, an add-on that you can purchase in addition to your GitHub subscription. If your codebase is open source, most of GHAS&#8217;s security features are <strong>free</strong> for use.</p><p>Here&#8217;s how to integrate and use these tools. You have to do this on a public repo if you haven&#8217;t purchased GHAS:</p><ul><li><p><strong>Integrating SAST tools in your workflow</strong>: Here&#8217;s how to set up code scanning with SAST tools:</p><ol><li><p>Navigate to the repository&#8217;s main page on GitHub.</p></li><li><p>Click on <strong>Security</strong> in the repository&#8217;s navigation bar.</p></li><li><p>Click <strong>Set up code scanning</strong>.</p></li><li><p>Choose a code scanning tool, such as CodeQL, and follow the prompts to configure it.</p></li></ol></li></ul><p>Examples of SAST tools include the following:</p><ul><li><p><strong>CodeQL</strong>: A powerful code analysis engine that scans your code for security vulnerabilities and coding errors</p></li><li><p><strong>Dependabot Alerts</strong>: Automatically scans your dependencies for known vulnerabilities and notifies you if any are found</p></li></ul><ul><li><p><strong>Interpreting scan results</strong>: To review and act on scan results, take the following steps:</p><ol><li><p>Go to the <strong>Security</strong> tab of the repository.</p></li><li><p>Click on <strong>Code scanning alerts</strong> to view the results.</p></li><li><p>Review the alerts and take appropriate action to fix the identified issues.</p></li></ol></li></ul><h2>CI/CD pipeline security measures</h2><p>Securing your <strong>Continuous Integration/Continuous Deployment</strong> (<strong>CI/CD</strong>) pipelines is crucial to ensure that your code remains secure throughout the development lifecycle. Follow these steps to secure your pipelines:</p><ul><li><p><strong>Securing CI/CD Pipelines Using GitHub Actions</strong>: To secure your CI/CD pipelines, take these steps:</p><ol><li><p>Navigate to the repository&#8217;s main page on GitHub.</p></li><li><p>Click on <strong>Actions</strong> in the repository&#8217;s navigation bar.</p></li><li><p>Set up workflows to include security checks, such as running SAST tools or dependency checks.</p></li></ol></li></ul><blockquote><p><strong>Mini-case example</strong></p><p>A development team noticed that their CI/CD pipeline was deploying code with outdated dependencies that had known vulnerabilities. The issue stemmed from a missing dependency scanning step in their GitHub Actions workflow. To mitigate this, they integrated Dependabot and added a step in their workflow to run <code>npm audit</code> during each build. This change helped catch vulnerable packages early and prevented insecure code from reaching production.</p></blockquote><ul><li><p><strong>Implementing secrets management in workflows</strong>: To manage secrets securely in your workflows, take these steps:</p><ol><li><p>Go to the repository&#8217;s main page on GitHub.</p></li><li><p>Click on <strong>Settings</strong>.</p></li><li><p>In the left sidebar, click <strong>Secrets and variables</strong>.</p></li><li><p>Then, select <strong>Actions</strong> from the submenu.</p></li><li><p>Click <strong>New repository secret</strong> to add secrets, such as API keys or tokens, securely.</p></li><li><p>Reference these secrets in your GitHub Actions workflows to avoid exposing sensitive information.</p></li></ol></li></ul><h2>Monitoring and auditing activities</h2><p>Regular monitoring and auditing of repository activities help you detect and respond to suspicious actions promptly. Here&#8217;s how to monitor and audit activities:</p><ul><li><p><strong>Using audit logs to monitor repository activities</strong>: To monitor repository activities, take these steps:</p><ol><li><p>Go to your organization&#8217;s main page on GitHub.</p></li><li><p>Click on <strong>Settings</strong>.</p></li><li><p>In the left sidebar, under the <strong>Archive</strong> section, click <strong>Logs</strong>,</p></li><li><p>Then, select <strong>Audit log</strong> from the submenu.</p></li><li><p>Review the audit log entries to monitor activities such as user logins, repository changes, and permission updates.</p></li></ol></li><li><p><strong>Setting up alerts for suspicious activities</strong>: To set up alerts for suspicious activities, take these steps:</p><ol><li><p>Use GitHub&#8217;s built-in security alerts to notify you of potential security issues.</p></li><li><p>Integrate with third-party monitoring tools to receive real-time alerts for suspicious activities.</p></li></ol></li></ul><h2>Incident response and recovery</h2><p>Being prepared for security incidents and having a plan for recovery is essential for minimizing the impact of security breaches. Follow these steps for incident response and recovery:</p><ul><li><p><strong>Preparing for security incidents</strong>: To prepare for security incidents, take these steps:</p><ol><li><p>Develop an incident response plan that outlines the steps to take in case of a security breach.</p></li><li><p>Ensure that all team members are aware of the plan and their roles in the response process.</p></li></ol></li><li><p><strong>Steps for incident response and recovery</strong>: To respond to and recover from security incidents, take these steps:</p><ol><li><p>Identify and contain the breach to prevent further damage.</p></li><li><p>Investigate the cause of the breach and assess the impact.</p></li><li><p>Remediate the vulnerabilities that led to the breach.</p></li><li><p>Communicate with stakeholders and provide updates on the incident and recovery efforts.</p></li><li><p>Review and update security policies and practices to prevent future incidents.</p></li></ol></li></ul><p>Et voila! This concludes the basics when it comes to security on GitHub. Let&#8217;s summarize what we learned.</p><div><hr></div><h1>Summary</h1><p>In this chapter, we delved into the intricacies of security practices and user management on GitHub. We had already mastered the essentials of effective collaboration, but we know that with great code comes great responsibility. We explored the robust security features GitHub offers, such as two-factor authentication, which added an extra layer of security to our accounts. We learned about the various methods available for 2FA, including authenticator apps, SMS, physical security keys, and GitHub Mobile.</p><p>We also discussed branch protection rules, which ensured our codebase remained stable and secure by enforcing workflows and requirements before changes could be merged. Additionally, we examined security configurations, both GitHub-recommended and custom, to enhance the security of our repositories. We looked at the management of security alerts and vulnerabilities through tools such as Dependabot, CodeQL, and secret scanning, which helped us stay on top of potential security issues.</p><p>Managing access and permissions was another crucial aspect we covered. We understood the importance of user roles and permissions at different levels, from enterprise to repository, and how to assign these roles effectively. We also explored the use of teams for access control, creating and managing teams to streamline our workflow. Finally, we looked at OAuth and personal access tokens, which provided secure ways to authenticate and authorize access to our GitHub account and repositories.</p><p>Overall, this chapter equipped us with the knowledge and tools to maintain a robust security posture and manage user access effectively on GitHub.</p><p>Let&#8217;s do a short quiz.</p><div><hr></div><h1>Test your knowledge</h1><p>Review all GitHub security and user management features, especially permission models, 2FA, and CI/CD hardening techniques &#8211; they appear frequently on the certification.</p><ol><li><p>Which of the following methods is considered the most secure for enabling <strong>Two-Factor Authentication</strong> (<strong>2FA</strong>) on GitHub?</p><ol><li><p><strong>Short Message Service</strong> (<strong>SMS</strong>)</p></li><li><p><strong>Time-Based One-Time Password</strong> (<strong>TOTP</strong>) authenticator apps</p></li><li><p>Virtual security keys</p></li><li><p>Physical security keys</p></li></ol></li><li><p>What is the primary purpose of <strong>Dependabot Security Updates</strong> in GitHub?</p><ol><li><p>To scan your code for security vulnerabilities and coding errors</p></li><li><p>To automatically generate pull requests to update vulnerable dependencies to secure versions</p></li><li><p>To detect and alert you if sensitive information is accidentally committed to your repository</p></li><li><p>To provide a centralized view of your security alerts and vulnerabilities across all repositories</p></li></ol></li><li><p>Which role in GitHub is best suited to managing security policies, security alerts, and security configurations for an organization and all its repositories?</p><ol><li><p>Owner</p></li><li><p>Security manager</p></li><li><p>Admin</p></li><li><p>CI/CD admin</p></li></ol></li></ol><div><hr></div><h1>Useful links</h1><ul><li><p>Authentication documentation: <a href="https://shorturl.at/jevdZ">https://docs.github.com/en/enterprise-cloud@latest/authentication</a></p></li><li><p>About GitHub security features: <a href="https://shorturl.at/RNF8Y">https://docs.github.com/en/enterprise-cloud@latest/code-security/getting-started/github-security-features#about-githubs-security-features</a></p></li><li><p>About GitHub Advanced Security: <a href="https://shorturl.at/7ZMJY">https://docs.github.com/en/enterprise-cloud@latest/get-started/learning-about-github/about-github-advanced-security</a></p></li></ul><div><hr></div><p>To build practical mastery of Git and GitHub&#8212; from version control basics to collaborative workflows, secure automation, and AI-assisted productivity&#8212;check out <em><strong><a href="https://www.packtpub.com/en-us/product/github-foundations-certification-guide-9781836206040">GitHub Foundations Certification Guide</a></strong></em> by Ayodeji Ayodele (Packt, 2025). Through step-by-step labs, real-world projects, and exam strategies, it helps you prepare for the GitHub Foundations certification while adopting best practices for issues and pull requests, GitHub Projects, privacy and security controls, and GitHub Copilot&#8212;so you can level up your skills and ship better software, faster.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.packtpub.com/en-us/product/github-foundations-certification-guide-9781836206040" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!c1C4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d177e6d-10ef-4e8d-9954-776efd177ad7_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!c1C4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d177e6d-10ef-4e8d-9954-776efd177ad7_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!c1C4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d177e6d-10ef-4e8d-9954-776efd177ad7_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!c1C4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d177e6d-10ef-4e8d-9954-776efd177ad7_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!c1C4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d177e6d-10ef-4e8d-9954-776efd177ad7_2250x2775" width="372" height="458.86813186813185" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9d177e6d-10ef-4e8d-9954-776efd177ad7_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:372,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;GitHub Foundations Certification Guide&quot;,&quot;title&quot;:&quot;GitHub Foundations Certification Guide&quot;,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.packtpub.com/en-us/product/github-foundations-certification-guide-9781836206040&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="GitHub Foundations Certification Guide" title="GitHub Foundations Certification Guide" srcset="https://substackcdn.com/image/fetch/$s_!c1C4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d177e6d-10ef-4e8d-9954-776efd177ad7_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!c1C4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d177e6d-10ef-4e8d-9954-776efd177ad7_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!c1C4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d177e6d-10ef-4e8d-9954-776efd177ad7_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!c1C4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d177e6d-10ef-4e8d-9954-776efd177ad7_2250x2775 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here&#8217;s what some readers have said:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cglw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab89407-eb5e-4431-afd7-b68983a5c8cd_866x485.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cglw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab89407-eb5e-4431-afd7-b68983a5c8cd_866x485.png 424w, https://substackcdn.com/image/fetch/$s_!cglw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab89407-eb5e-4431-afd7-b68983a5c8cd_866x485.png 848w, https://substackcdn.com/image/fetch/$s_!cglw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab89407-eb5e-4431-afd7-b68983a5c8cd_866x485.png 1272w, https://substackcdn.com/image/fetch/$s_!cglw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab89407-eb5e-4431-afd7-b68983a5c8cd_866x485.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cglw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab89407-eb5e-4431-afd7-b68983a5c8cd_866x485.png" width="866" height="485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fab89407-eb5e-4431-afd7-b68983a5c8cd_866x485.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:485,&quot;width&quot;:866,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:150335,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/178064311?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e55c92f-6989-49dc-9cdc-702306914746_885x485.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!cglw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab89407-eb5e-4431-afd7-b68983a5c8cd_866x485.png 424w, https://substackcdn.com/image/fetch/$s_!cglw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab89407-eb5e-4431-afd7-b68983a5c8cd_866x485.png 848w, https://substackcdn.com/image/fetch/$s_!cglw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab89407-eb5e-4431-afd7-b68983a5c8cd_866x485.png 1272w, https://substackcdn.com/image/fetch/$s_!cglw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab89407-eb5e-4431-afd7-b68983a5c8cd_866x485.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VEfR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0b9a3ff-7f6b-44c5-8c4e-a8bf341e5845_871x386.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VEfR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0b9a3ff-7f6b-44c5-8c4e-a8bf341e5845_871x386.png 424w, https://substackcdn.com/image/fetch/$s_!VEfR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0b9a3ff-7f6b-44c5-8c4e-a8bf341e5845_871x386.png 848w, https://substackcdn.com/image/fetch/$s_!VEfR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0b9a3ff-7f6b-44c5-8c4e-a8bf341e5845_871x386.png 1272w, https://substackcdn.com/image/fetch/$s_!VEfR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0b9a3ff-7f6b-44c5-8c4e-a8bf341e5845_871x386.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VEfR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0b9a3ff-7f6b-44c5-8c4e-a8bf341e5845_871x386.png" width="871" height="386" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f0b9a3ff-7f6b-44c5-8c4e-a8bf341e5845_871x386.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:386,&quot;width&quot;:871,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:84580,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/178064311?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0b9a3ff-7f6b-44c5-8c4e-a8bf341e5845_871x386.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!VEfR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0b9a3ff-7f6b-44c5-8c4e-a8bf341e5845_871x386.png 424w, https://substackcdn.com/image/fetch/$s_!VEfR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0b9a3ff-7f6b-44c5-8c4e-a8bf341e5845_871x386.png 848w, https://substackcdn.com/image/fetch/$s_!VEfR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0b9a3ff-7f6b-44c5-8c4e-a8bf341e5845_871x386.png 1272w, https://substackcdn.com/image/fetch/$s_!VEfR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0b9a3ff-7f6b-44c5-8c4e-a8bf341e5845_871x386.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Jch1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8328e5f-7901-4408-8343-78bba89ca87a_856x388.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Jch1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8328e5f-7901-4408-8343-78bba89ca87a_856x388.png 424w, https://substackcdn.com/image/fetch/$s_!Jch1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8328e5f-7901-4408-8343-78bba89ca87a_856x388.png 848w, https://substackcdn.com/image/fetch/$s_!Jch1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8328e5f-7901-4408-8343-78bba89ca87a_856x388.png 1272w, https://substackcdn.com/image/fetch/$s_!Jch1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8328e5f-7901-4408-8343-78bba89ca87a_856x388.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Jch1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8328e5f-7901-4408-8343-78bba89ca87a_856x388.png" width="856" height="388" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d8328e5f-7901-4408-8343-78bba89ca87a_856x388.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:388,&quot;width&quot;:856,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:95238,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/178064311?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8328e5f-7901-4408-8343-78bba89ca87a_856x388.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Jch1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8328e5f-7901-4408-8343-78bba89ca87a_856x388.png 424w, https://substackcdn.com/image/fetch/$s_!Jch1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8328e5f-7901-4408-8343-78bba89ca87a_856x388.png 848w, https://substackcdn.com/image/fetch/$s_!Jch1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8328e5f-7901-4408-8343-78bba89ca87a_856x388.png 1272w, https://substackcdn.com/image/fetch/$s_!Jch1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8328e5f-7901-4408-8343-78bba89ca87a_856x388.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Introduction to Application Development Frameworks]]></title><description><![CDATA[The complete "Chapter 1: Introduction to Application Development Frameworks" from the book, Building an Application Development Framework (Packt, 2025), by Ivan Padabed and Roman Voronin]]></description><link>https://deepengineering.substack.com/p/introduction-to-application-development</link><guid isPermaLink="false">https://deepengineering.substack.com/p/introduction-to-application-development</guid><dc:creator><![CDATA[Roman Voronin]]></dc:creator><pubDate>Thu, 23 Oct 2025 07:00:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!whlm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa807bff8-4b44-4477-8828-fcec5f73cfe3_2250x2775" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this <strong><a href="https://www.packtpub.com/en-us/product/building-an-application-development-framework-9781836208570">book</a></strong> we will be delving into the different aspects of an <strong>Application Development Framework </strong>(<strong>ADF</strong>) lifecycle, allowing individual software engineers, development teams, and engineering organizations to benefit from ADF&#8217;s great potential. The initial chapter of the book is focused on providing a wide context for future chapters, setting a common ground for all ADF stakeholders, and introducing basic classifications and definitions for future use.</p><p>The concept of Application Development Framework (ADF) has been well-known for a long time, but we need to set up a crystal-clear context for further reading. This is important because it helps us deal with this complex topic by setting common ground for definitions and classifications that will be used throughout the book. First, we explore the evolution of the idea of ADF. After that, we discover the differences and connections between other SDLC-focused technologies, such as Platforms, Libraries, SDKs, and APIs, to craft a brief but concise definition that helps us keep a big picture while diving deep into implementation topics. Then, we review the place of ADF in the Software Development Lifecycle to identify and prove the advantages of adopting an ADF.</p><p>In this chapter we&#8217;re going to cover the following main topics:</p><ul><li><p>Introduction and historical references</p></li><li><p>Breaking down Application Development Framework</p></li><li><p>Exploring ADF and Platforms, Libraries, SDKs, APIs</p></li><li><p>Integrating into Software Development Lifecycle (SDLC) and Flow</p></li><li><p>Differentiating ADF and other types of Frameworks</p></li></ul><h1>Introduction and historical references</h1><p>Engineers have a long and productive history of creating building blocks for their own convenience. If we do not ignore this historical experience, we can learn many useful lessons for creating our own frameworks.</p><p>From the very beginning of the software industry, engineers and scientists have had a tendency to reuse their most successful and efficient ideas. There are quite a few historical practices that share the core objectives of a framework:</p><ul><li><p><strong>Architectural Blueprints</strong>: Since ancient times, complex structures like buildings or ships were built based on detailed plans. These plans defined the overall structure, components, and relationships - similar to how frameworks provide a blueprint for software architecture.</p></li><li><p><strong>Modular Design in Engineering</strong>: Even before the computer age, engineers approached complex machines with a modular mindset. Think of early steam engines with interchangeable parts - a principle that carries over to software components within a framework.</p></li><li><p><strong>Mathematical Frameworks</strong>: For centuries, mathematicians have relied on established frameworks like algebra or calculus to solve problems. These frameworks provide a set of rules and structures that guide the approach to solving a specific type of problem.</p></li></ul><p>While these aren&#8217;t direct equivalents to software frameworks, they all represent historical approaches to structuring complex systems in a way that aligns with the core function of a software development framework&#8212;to simplify the lives of its users when dealing with complex problems.</p><p>With the advent and adoption of computers, the concept of a framework has gone beyond the art of the elite and has become part of the daily work of many programmers. The idea evolved as computers themselves developed. Here are some contenders for the title of earliest software framework:</p><ul><li><p><strong>Early Subroutine Libraries (1940s &#8211; 1950s)</strong>: In the early days of computing, programmers might develop reusable code blocks for common tasks like mathematical functions or input/output routines. These weren&#8217;t full-fledged frameworks, but they offered a basic level of reusability and structure.</p></li><li><p><strong>FORTRAN Compilers (1950s)</strong>: FORTRAN introduced the concept of high-level languages, allowing programmers to write code that is more human-readable than machine code. While not exactly a framework, it provided a foundational structure for building software.</p></li><li><p><strong>Operating Systems (1960s onwards)</strong>: Operating systems like IBM&#8217;s OS/360 offered a platform for running applications. They provided core functionalities like memory management and device drivers, which later frameworks were built upon.</p></li></ul><p>It&#8217;s important to remember that the concept of a software development framework as we know it today &#8211; offering a comprehensive set of tools, libraries, and design patterns &#8211; is a more recent development. However, these earlier practices laid the groundwork for the frameworks we use today. In the modern world, we can only imagine the practical purpose software created on top of one or multiple frameworks.</p><blockquote><p><strong>Note</strong></p><p>This book uses both terms &#8220;software development framework (SDF)&#8221; and &#8220;application development framework (ADF)&#8221; interchangeably. Usually, &#8220;application&#8221; is not exactly the same as &#8220;software&#8221;: we have platforms, libraries, SDKs, engineering tools and frameworks as alternative kinds of software. But in the context of the topic (&#8220;building frameworks&#8221;) we can always safely assume that any &#8220;software&#8221; we are going to develop with our frameworks will serve the same purpose as &#8220;application&#8221; with a minor exception of &#8220;infrastructure management frameworks&#8221; which are mentioned explicitly.</p></blockquote><p>While most of the information on the internet about Application Development Frameworks is focused on web and mobile development, we cannot ignore trending frameworks from a non-application software, such as</p><ul><li><p>Artificial Intelligence and Machine Learning (like PyTorch, TensorFlow, and Apache MXNet),</p></li><li><p>Scheduled task management (like Celery, Temporal, and Apache Airflow),</p></li><li><p>Infrastructure management (like Terraform, Pulumi, and Crossplane),</p></li><li><p>Testing automation (like Selenium, Robot, and webdriverIO),</p></li></ul><p>and many others, including &#8220;hybrid&#8221; frameworks that provide multiple capabilities at once.</p><p>Fortunately, foundational principles of building software frameworks are common between classic ADF and these emerging types of SDF.</p><p>There are also vertical ADFs, aiming to cover corresponding business domains. Examples of such frameworks include Gamedev (Flame, Monogame), Data Visualization (Shiny, Seaborn, TensorBoard), Hardware Instrumentation (LabVIEW), etc.</p><p>A <a href="https://survey.stackoverflow.co/2023/#section-most-loved-dreaded-and-wanted-web-frameworks">StackOverflow research in 2023</a> that involved approximately 90,000 software engineers provided us with data about frameworks they use daily (See <em>Figure 1.1</em> that summarizes one of the framework-related topics from this survey). In addition to those numbers, we know that many frameworks have their own communities outside of StackOverflow, which means that more than 100k engineers work with software frameworks on a daily basis.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!J6iQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03053d91-c55a-40eb-96f6-a000cabd4e76_666x1079.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!J6iQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03053d91-c55a-40eb-96f6-a000cabd4e76_666x1079.png 424w, https://substackcdn.com/image/fetch/$s_!J6iQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03053d91-c55a-40eb-96f6-a000cabd4e76_666x1079.png 848w, https://substackcdn.com/image/fetch/$s_!J6iQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03053d91-c55a-40eb-96f6-a000cabd4e76_666x1079.png 1272w, https://substackcdn.com/image/fetch/$s_!J6iQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03053d91-c55a-40eb-96f6-a000cabd4e76_666x1079.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!J6iQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03053d91-c55a-40eb-96f6-a000cabd4e76_666x1079.png" width="666" height="1079" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/03053d91-c55a-40eb-96f6-a000cabd4e76_666x1079.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1079,&quot;width&quot;:666,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 1.4: StackOverflow research summary chart&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 1.4: StackOverflow research summary chart" title="Figure 1.4: StackOverflow research summary chart" srcset="https://substackcdn.com/image/fetch/$s_!J6iQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03053d91-c55a-40eb-96f6-a000cabd4e76_666x1079.png 424w, https://substackcdn.com/image/fetch/$s_!J6iQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03053d91-c55a-40eb-96f6-a000cabd4e76_666x1079.png 848w, https://substackcdn.com/image/fetch/$s_!J6iQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03053d91-c55a-40eb-96f6-a000cabd4e76_666x1079.png 1272w, https://substackcdn.com/image/fetch/$s_!J6iQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03053d91-c55a-40eb-96f6-a000cabd4e76_666x1079.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 1.1: StackOverflow research summary chart</em></p><p>With all the wide adoption of ADFs, it is confusing to see how many different inconsistent definitions and classifications we have all around the internet. In the following section, we will craft a brief and concise definition based on ADFs unique differentiators in a world of software engineering.</p><h1>Breaking down Application Development Framework</h1><p>I often see engineers mixing up libraries, frameworks, and Software Development Kits. They use these words like they mean the same thing. Even more confusion can come if we add APIs, platforms, and DSLs to the conversation. But to make our own framework, we need to understand all these things.</p><p>According to Dictionary.com, the formal definition of the term &#8220;framework&#8221; is <em>&#8220;a basic structure, plan, or system, as of concepts, values, customs, or rules&#8221;.</em></p><p>The collective unconscious of humanity, also known as LLM, suggests the following definition for ADF: <em>An application development framework is a software library offering a fundamental structure for building applications within a specific environment. It acts as a reusable foundation, supplying pre-defined functionalities and promoting code organization through established conventions. This approach streamlines development by reducing repetitive coding efforts</em>.</p><p>Both definitions are formally correct (except for &#8220;library&#8221; part of the second one, which I will explain later in this section). But they focus on how the framework is designed but have a lack of explanation about how it works. I am going to fill this gap by adding my own:</p><blockquote><p><strong>Definition</strong></p><p>A collection of pre-written code and tools that provide a structured approach to building applications. It simplifies development by enforcing architectural patterns: frameworks always dictate an execution flow, and stipulate specific way to structure your code, promoting maintainability, testability, low coupling, and reusability.</p></blockquote><p>It is a common misunderstanding to confuse frameworks with other engineering concepts aiming towards reusability, like API, software library, SDK, and platform. And there is always a special &#8220;tooling&#8221; category, which covers a wide range of software from smaller console scripts to powerful configurable logs processing pipelines &#8211; they all live their own life as they only used by developers to support their routine tasks, so we keep them out of conversation. Let&#8217;s set clear boundaries to understand their differences to focus on the most important aspects of our topic.</p><h2>Application Programming Interface</h2><p>Starting from <strong>API</strong> (<strong>Application Programming Interface</strong>) as the lowest-level implementation of the development tooling. The traditional understanding of API included any exposed interface available to software developers to perform manipulation with an external subsystem. This external subsystem was treated as a &#8220;black box,&#8221; which means that the developer should not worry about its internal implementation, tech stack, and logic. Thus, API provides a complete set of methods to deal with it. Modern understanding of the API concept drifted towards over-the-network API, like HTTP/gRPC/websocket APIs. Events and message-based communication interfaces are also subsets of APIs &#8211; like webhooks.</p><p>The best practice of API definition is to use open standards like OpenAPI and AsyncAPI schema languages, or other less popular languages like RAML.org or APIBlueprint.org. However, it is acceptable to use proprietary or vendor-specific tools. API concept can also be visualized with a simple diagram notation (see Figure 1.2 below). Typical representatives of the API are as follows:</p><ul><li><p>SaaS products&#8217; interfaces, e.g. <a href="https://developers.pandadoc.com/reference/about">PandaDoc API</a>, or <a href="https://platform.openai.com/docs/api-reference/introduction">OpenAI LLM API</a></p></li><li><p>Cloud management APIs; e.g. <a href="https://docs.aws.amazon.com/cloudcontrolapi/latest/APIReference/Welcome.html">AWS Cloud Control API</a> , or <a href="https://learn.microsoft.com/en-us/rest/api/azure/">Azure REST API</a>, or Google&#8217;s <a href="https://cloud.google.com/service-infrastructure/docs/service-management/reference/rest">Service Management API</a></p></li><li><p>Webhooks, like <a href="https://zapier.com/blog/what-are-webhooks/">Zapier</a></p></li><li><p>Internal/proprietary messaging-based events and commands schema registries that can be based on Confluent or <a href="https://docs.redpanda.com/current/manage/schema-reg/schema-reg-overview/">Redpanda</a> </p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!u3c7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb787246d-f808-436d-b465-c754904f58ec_366x197.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u3c7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb787246d-f808-436d-b465-c754904f58ec_366x197.png 424w, https://substackcdn.com/image/fetch/$s_!u3c7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb787246d-f808-436d-b465-c754904f58ec_366x197.png 848w, https://substackcdn.com/image/fetch/$s_!u3c7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb787246d-f808-436d-b465-c754904f58ec_366x197.png 1272w, https://substackcdn.com/image/fetch/$s_!u3c7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb787246d-f808-436d-b465-c754904f58ec_366x197.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!u3c7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb787246d-f808-436d-b465-c754904f58ec_366x197.png" width="366" height="197" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b787246d-f808-436d-b465-c754904f58ec_366x197.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:197,&quot;width&quot;:366,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 1.5: Concept-level diagram of API&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 1.5: Concept-level diagram of API" title="Figure 1.5: Concept-level diagram of API" srcset="https://substackcdn.com/image/fetch/$s_!u3c7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb787246d-f808-436d-b465-c754904f58ec_366x197.png 424w, https://substackcdn.com/image/fetch/$s_!u3c7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb787246d-f808-436d-b465-c754904f58ec_366x197.png 848w, https://substackcdn.com/image/fetch/$s_!u3c7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb787246d-f808-436d-b465-c754904f58ec_366x197.png 1272w, https://substackcdn.com/image/fetch/$s_!u3c7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb787246d-f808-436d-b465-c754904f58ec_366x197.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 1.2: Concept-level diagram of API</em></p><p>The diagram helps to see that the API purpose is to provide access to exposed &#8220;black box&#8221; functions.</p><h2>Library</h2><p>The next one to review is a <strong>software library</strong> as a <em>collection of pre-written code or routines</em> that developers can use to perform specific tasks or functions within their software applications.</p><p>Sometimes developers see any software library as a framework, but the purpose of the library is completely different &#8211; it focuses on runtime/operation concerns by implementing a common part of the system, like hardware I/O operations, network protocol, authorization sequence, ranking algorithm, IoT standard, etc. It is also common to have a library to transform a low-level API into a more developer-friendly form by adding enumerables, constants, and conditional logic over a binary code and method signatures of plain API. A Library usually operates as a <strong>gray box,</strong> which means that software developers can see its internal implementation, but it is rarely necessary. In some cases, libraries can come in binary format, which makes them <strong>black boxes</strong>.</p><p>Terminology across the industry is not always consistent, we can find other synonyms for the term &#8220;software library&#8221;:</p><ul><li><p><strong>Package</strong>; usually means one or multiple software libraries that share the same license and they can be distributed as a single unit.</p></li><li><p><strong>Module</strong>; usually means a built-in software library, distributed with the program.</p></li><li><p><strong>Extension </strong>(aka add-on or plug-in); usually means a software library that follows specific program interface allowing external developers to modify original program behavior without changing any code in the original system.</p></li></ul><p>Another important consideration is <strong>control flow</strong>. For a software library, it is common for developers to have full control over the library functions &#8211; so developers are responsible for invoking the library.</p><p>Writing software libraries is one of the most common tasks in the industry; many senior developers have experience of creating libs for internal company purposes, or contributing to open-source libs, or at least have them as a part of their pet projects.</p><p>Often, a software library evolves into an SDK or a framework after multiple iterations of improvements. And we definitely need to build libraries as part of the ADF development.</p><p>To understand the idea of software library better, we can use real examples:</p><ul><li><p>Algorithm libraries like <a href="https://en.wikipedia.org/wiki/List_of_numerical_libraries">math</a>, or <a href="https://en.wikipedia.org/wiki/List_of_3D_graphics_libraries">3D</a>, or <a href="https://en.wikipedia.org/wiki/Category:Python_(programming_language)_scientific_libraries">ML</a></p></li><li><p>Hardware abstraction libraries like <a href="https://infineon.github.io/psoc6hal/html/index.html">HAL</a> or <a href="https://www.geeksforgeeks.org/operating-systems/device-driver-and-its-purpose/">device drivers</a> </p></li><li><p>Standard-compliant implementations like <a href="https://openauth.js.org/">OpenAuth</a></p></li><li><p>Programming helpers like <a href="https://www.boost.org/">Boost</a> or <a href="https://github.com/psf/requests">Requests.py</a> that provide developers with a pre-written code for HTTP requests lifecycle syntaxis helper.</p></li><li><p>Any proprietary pluggable reusable code</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1p3e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfa81e6b-268c-475d-9b8e-b3f1f1e42523_497x177.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1p3e!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfa81e6b-268c-475d-9b8e-b3f1f1e42523_497x177.png 424w, https://substackcdn.com/image/fetch/$s_!1p3e!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfa81e6b-268c-475d-9b8e-b3f1f1e42523_497x177.png 848w, https://substackcdn.com/image/fetch/$s_!1p3e!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfa81e6b-268c-475d-9b8e-b3f1f1e42523_497x177.png 1272w, https://substackcdn.com/image/fetch/$s_!1p3e!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfa81e6b-268c-475d-9b8e-b3f1f1e42523_497x177.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1p3e!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfa81e6b-268c-475d-9b8e-b3f1f1e42523_497x177.png" width="497" height="177" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bfa81e6b-268c-475d-9b8e-b3f1f1e42523_497x177.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:177,&quot;width&quot;:497,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 1.6: Concept-level diagram of API and Library&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 1.6: Concept-level diagram of API and Library" title="Figure 1.6: Concept-level diagram of API and Library" srcset="https://substackcdn.com/image/fetch/$s_!1p3e!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfa81e6b-268c-475d-9b8e-b3f1f1e42523_497x177.png 424w, https://substackcdn.com/image/fetch/$s_!1p3e!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfa81e6b-268c-475d-9b8e-b3f1f1e42523_497x177.png 848w, https://substackcdn.com/image/fetch/$s_!1p3e!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfa81e6b-268c-475d-9b8e-b3f1f1e42523_497x177.png 1272w, https://substackcdn.com/image/fetch/$s_!1p3e!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbfa81e6b-268c-475d-9b8e-b3f1f1e42523_497x177.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 1.3: Concept-level diagram of API and Library</em></p><p>The diagram suggests that Library can serve as a pre-implemented tool to integrate an API to the application; but basically, it can provide any pre-implemented code for reuse.</p><h2>Software Development Kit</h2><p>Similar functions can be also performed by <strong>Software Development Kits</strong> (<strong>SDKs</strong>) but they usually include much more than just a software Libraries; there is a list of possible SDK internals:</p><ul><li><p>Libraries.</p></li><li><p>Tracing and Debugging tools.</p></li><li><p>Documentation.</p></li><li><p>Integrated development environments (IDEs)</p></li><li><p>Tests.</p></li><li><p>Plug-ins.</p></li><li><p>Application programming interfaces (APIs)</p></li><li><p>Sample code.</p></li></ul><p>SDKs span both design-time (organizational) and runtime/operations (product) concerns but still with focus on a runtime. SDKs are also platform&#8211; or vendor-specific, they are developed by API or Platform vendors to improve their products adoption &#8211; see Android SDK created by Google (Alphabet) and Windows ASDK developed by Microsoft.</p><p>Sometimes bigger SDKs can be designed to include frameworks (like Apple SDK), but we can also see the opposite case, where an SDK is designed as an element of the framework. The following are examples of cases where SDKs are subsystems of ADFs in the list below:</p><ul><li><p><a href="https://sdk.operatorframework.io/">Operator SDK</a> is part of the Operator Framework</p></li><li><p>SDKs as developer-friendly lib wrappers for a particular framework, (for example) <a href="https://github.com/Treblle/treblle-python">Treblle</a> provides multiple SDKs including one for the Django framework</p></li><li><p>SDKs have the same control flow as libraries have: the developer is responsible for invoking an SDK, while in the case of frameworks, we usually have the opposite control flow: the framework is responsible for invoking developer&#8217;s code. There is no exception if the framework is part of an SDK: the framework takes ownership over the control flow.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WFSx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febc13f6a-9311-4c69-95a2-2661bf8be3c5_589x201.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WFSx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febc13f6a-9311-4c69-95a2-2661bf8be3c5_589x201.png 424w, https://substackcdn.com/image/fetch/$s_!WFSx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febc13f6a-9311-4c69-95a2-2661bf8be3c5_589x201.png 848w, https://substackcdn.com/image/fetch/$s_!WFSx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febc13f6a-9311-4c69-95a2-2661bf8be3c5_589x201.png 1272w, https://substackcdn.com/image/fetch/$s_!WFSx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febc13f6a-9311-4c69-95a2-2661bf8be3c5_589x201.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WFSx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febc13f6a-9311-4c69-95a2-2661bf8be3c5_589x201.png" width="589" height="201" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ebc13f6a-9311-4c69-95a2-2661bf8be3c5_589x201.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:201,&quot;width&quot;:589,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 1.7: Concept diagram of API, Library, and SDK&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 1.7: Concept diagram of API, Library, and SDK" title="Figure 1.7: Concept diagram of API, Library, and SDK" srcset="https://substackcdn.com/image/fetch/$s_!WFSx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febc13f6a-9311-4c69-95a2-2661bf8be3c5_589x201.png 424w, https://substackcdn.com/image/fetch/$s_!WFSx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febc13f6a-9311-4c69-95a2-2661bf8be3c5_589x201.png 848w, https://substackcdn.com/image/fetch/$s_!WFSx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febc13f6a-9311-4c69-95a2-2661bf8be3c5_589x201.png 1272w, https://substackcdn.com/image/fetch/$s_!WFSx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Febc13f6a-9311-4c69-95a2-2661bf8be3c5_589x201.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 1.4: Concept diagram of API, Library, and SDK</em></p><p>The diagram above depicts the SDK as a super-entity for a library.</p><h2>Framework</h2><p>Let&#8217;s elaborate on our definition here:</p><blockquote><p><em>A framework is a collection of pre-written code and tools that provides a structured approach to building applications. It simplifies development by enforcing architectural patterns: frameworks always dictate an execution flow, and stipulate specific way to structure your code, promoting maintainability, testability, low coupling, and reusability.</em></p></blockquote><p>Of course, frameworks provide more than that, there are some examples below:</p><ul><li><p>Hide low-level complexity behind a higher-level abstraction;</p></li><li><p>Promote faster development by providing pre-built binary/packaged components and functionalities;</p></li></ul><p>But those additional benefits cannot be attributed exclusively to frameworks &#8211; libraries or SDKs both have the same value propositions.</p><p>The following are the Key aspects of this definition based on usage scenarios and key attributes:</p><ul><li><p><strong>&#8220;Framework as abstraction&#8221;</strong> conceals repetitive code and low-level details by applying software libraries, acting as a higher-level interface. This allows developers to work with core functionalities without getting bogged down in implementation specifics. However, it usually gives an option of direct communication with levels under even if it is unnecessarily for overwhelming majority of scenarios; <em>in brief, any framework has one or multiple libraries coming as a built-in option or pluggable 3rd-parties.</em></p></li><li><p><strong>&#8220;Frameworks as tooling&#8221;</strong> prioritize simplifying the development process by providing pre-built components, streamlined workflows, and reduced boilerplate code. Their primary focus is on accelerating development, while runtime considerations (like operations, maintenance, portability, performance) are a secondary benefit.</p></li><li><p><strong>&#8220;Framework as architectural constraint&#8221;</strong> establishes a blueprint for system architecture. It dictates core components, their interactions, and overall structure, influencing key design decisions for developers working within the framework&#8217;s constraints.</p></li></ul><p>And finally, the relations between a frameworks and APIs, libraries and SDKs are usually follow the common pattern: ADF streamlines the software development flow for the organization, governs the control flow by invoking a custom code made by software developer, having libraries as a proxy to access external subsystem APIs, and allowing to plug in a third-party libraries or SDKs to handle specific integrations.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1x5h!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a8baefe-9549-4782-b757-4ec9fbe7c61a_716x424.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1x5h!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a8baefe-9549-4782-b757-4ec9fbe7c61a_716x424.png 424w, https://substackcdn.com/image/fetch/$s_!1x5h!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a8baefe-9549-4782-b757-4ec9fbe7c61a_716x424.png 848w, https://substackcdn.com/image/fetch/$s_!1x5h!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a8baefe-9549-4782-b757-4ec9fbe7c61a_716x424.png 1272w, https://substackcdn.com/image/fetch/$s_!1x5h!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a8baefe-9549-4782-b757-4ec9fbe7c61a_716x424.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1x5h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a8baefe-9549-4782-b757-4ec9fbe7c61a_716x424.png" width="716" height="424" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a8baefe-9549-4782-b757-4ec9fbe7c61a_716x424.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:424,&quot;width&quot;:716,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 1.8: Concept diagram of ADF, SDK, Library, and API&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 1.8: Concept diagram of ADF, SDK, Library, and API" title="Figure 1.8: Concept diagram of ADF, SDK, Library, and API" srcset="https://substackcdn.com/image/fetch/$s_!1x5h!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a8baefe-9549-4782-b757-4ec9fbe7c61a_716x424.png 424w, https://substackcdn.com/image/fetch/$s_!1x5h!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a8baefe-9549-4782-b757-4ec9fbe7c61a_716x424.png 848w, https://substackcdn.com/image/fetch/$s_!1x5h!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a8baefe-9549-4782-b757-4ec9fbe7c61a_716x424.png 1272w, https://substackcdn.com/image/fetch/$s_!1x5h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a8baefe-9549-4782-b757-4ec9fbe7c61a_716x424.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 1.5: Concept diagram of ADF, SDK, Library, and API</em></p><p>There is an extended classification of the Application Development Frameworks:</p><ul><li><p>Web frameworks like Django, Node.js, Java Play, Ruby-on-Rails, etc</p></li><li><p>Enterprise frameworks like Java Spring, Oracle ADF</p></li><li><p><a href="https://github.com/topics/low-code-framework">Low-code frameworks</a> like Flutter, OpenBlocks, Appsmith</p></li><li><p>AI/ML frameworks like TensorFlow, Keras, Apache MXNet</p></li><li><p>Gamedev frameworks like Unity, UnrealEngine</p></li><li><p>Mobile frameworks like React Native, Xamarin, Apache Cordova</p></li><li><p>Microservice frameworks like GoMicro, Spring Boot, Molecular</p></li><li><p>Test automation frameworks like Selenium, Appium, WebdriverIO</p></li><li><p>Desktop OS frameworks like MFC, OS X framework, KDE framework</p></li><li><p>Utility frameworks like Python Celery, ActiveTask,</p></li><li><p><em>Custom frameworks &#8211; proprietary ones build for internal use, usually applying ADF format for a domain objects and rules</em></p></li><li><p>As Frameworks are our focus area, we add more detailed specification for three ADFs to better highlight their commonalities.</p></li></ul><p>The first one to analyze is Django:</p><ul><li><p>It employs <a href="https://djangopackages.org/">numerous libraries</a> in pluggable way</p></li></ul><ul><li><p>It focuses on design-time aspects: &#8220;<a href="https://www.djangoproject.com/">encourages rapid development and clean, pragmatic design</a>&#8221; </p></li><li><p>It is responsible for a control flow &#8211; developers don&#8217;t need to invoke Django code but to follow a Django project structure to get their code invoked in a right moment;</p></li><li><p>It enforces multiple architecture patterns (MVC / MVT as a model-view-template, ORM as object-relational mapping, extendable middleware-based request processing pipeline, class-based views, etc)</p></li><li><p>It is vendor- and platform-neutral so it can be used on any cloud platform or a virtual machine that can interpret Python programming language;</p></li><li><p>It is a &#8220;gray box&#8221; software that can be <a href="https://code.djangoproject.com/wiki/Distributions">redistributed</a> as a package but it also has its <a href="https://github.com/django/django">source code</a> published in a public GitHub repository under the BSD-3 OSS license</p></li></ul><p>The second one is <a href="https://nodejs.org">Node.js</a>, the most popular web full-stack framework based on the JavaScript programming language:</p><ul><li><p>It has number of <a href="https://nodejs.org/docs/latest-v12.x/api/">standard built-in libraries</a> listed in official documentation and hundreds of pluggable external libraries like listed <a href="https://github.com/sindresorhus/awesome-nodejs">here</a></p></li><li><p>Org design-time focus is clearly emphasized as a key success factor of this framework; &#8230;</p><blockquote><p>(As <a href="https://the-stack-overflow-podcast.simplecast.com/episodes/why-the-creator-of-nodejs-created-a-new-javascript-runtime/transcript">Ryan Dahl said</a>): &#8220;<em>So for kind of technical reasons, adding a server onto JavaScript worked really well and people who were programming front end websites were able to take those same skills and with just a small amount of additional knowledge were able to program pretty nice web servers that could do long polling or other kinds of real time interactions. And I think there&#8217;s just a large base of JavaScript users out there, naturally, it being the language of the web, and so there was a lot of people who were able to take their skills and add on Node to that and suddenly become full stack developers</em>.&#8221; </p></blockquote></li><li><p>It is responsible for the control flow</p></li><li><p>It enforces architecture patterns like event-driven, microservices, API-first etc.</p></li><li><p>It is vendor- and platform-neutral</p></li><li><p>It is a &#8220;gray box&#8221; open-source software</p></li></ul><p>And the final one is <a href="https://react.dev/">React</a>, a modern web front-end framework based on the JavaScript language:</p><ul><li><p>Dozens of libraries like <a href="https://www.reactlibraries.com/search?qType=libraries&amp;q=*">here</a></p></li><li><p>Design-time focus: most of public sources mention developer-oriented benefits as a key advantage of the framework; this list includes declarative syntaxis, reusable components, community support, detailed documentation, etc.</p></li><li><p>Control flow management based on a virtual DOM concept is the core of the framework</p></li><li><p>React&#8217;s intrinsic architecture patterns include event-driven (hooks), container-based decorators, data repository (provider), etc.</p></li><li><p>It is vendor- and platform-neutral</p></li><li><p>It is a &#8220;gray box&#8221; open-source software</p></li></ul><p>As we can see, most ADFs follow the same model which we will explore in more detail in <em>Chapter 3</em>.</p><h2>Platform</h2><p>And the final concept to review here is a <strong>Platform</strong>. The main differentiator of a Platform, in comparison with all the others: APIs, libraries, SDKs and frameworks, is its hosted server-side execution runtime. But it is important to understand that the runtime concern is a &#8220;bonus value&#8221; here because the fundamental benefits of Platforms are still organizational design-time toolings. Platforms usually combine that hosted execution backend with APIs, libraries, and SDKs; and it is common for modern Platforms to provide developers with more advanced tooling like dev portals, resource management console and UI, cloud IDE, infrastructure-as-a-code definitions support and many other org productivity boosters. However, platforms rarely include application development frameworks and vice versa. We still can see frameworks being part of the platform (like AWS Well-Architected Framework is part of AWS platform value proposition, and <a href="https://learn.microsoft.com/en-us/azure/well-architected/">Microsoft </a>also has the same one), but these are not ADFs but architecture design frameworks (set of values, viewpoints, blueprints and patterns for cloud-native applications).</p><p>Typical taxonomy with examples of the Platforms is the following:</p><ul><li><p>Cloud platforms like AWS, MS Azure or GCP</p></li><li><p>Messaging platforms like Confluent Kafka or Redpanda</p></li><li><p>Task execution platforms like Temporal.io</p></li><li><p>Robotic process automation (RPA) platforms like WorkFusion or UIPath</p></li><li><p>Game Platforms like <a href="https://heroiclabs.com/heroic-cloud/">Heroic Cloud</a></p></li><li><p>Dev platforms like Split.io, Firebase, Launchdarkly</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-9Z8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d718fe8-d32a-4d13-825c-22911b16a496_716x424.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-9Z8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d718fe8-d32a-4d13-825c-22911b16a496_716x424.png 424w, https://substackcdn.com/image/fetch/$s_!-9Z8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d718fe8-d32a-4d13-825c-22911b16a496_716x424.png 848w, https://substackcdn.com/image/fetch/$s_!-9Z8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d718fe8-d32a-4d13-825c-22911b16a496_716x424.png 1272w, https://substackcdn.com/image/fetch/$s_!-9Z8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d718fe8-d32a-4d13-825c-22911b16a496_716x424.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-9Z8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d718fe8-d32a-4d13-825c-22911b16a496_716x424.png" width="716" height="424" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4d718fe8-d32a-4d13-825c-22911b16a496_716x424.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:424,&quot;width&quot;:716,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 1.9: Concept diagram of Platform, Framework, SDK, Library, and API&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 1.9: Concept diagram of Platform, Framework, SDK, Library, and API" title="Figure 1.9: Concept diagram of Platform, Framework, SDK, Library, and API" srcset="https://substackcdn.com/image/fetch/$s_!-9Z8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d718fe8-d32a-4d13-825c-22911b16a496_716x424.png 424w, https://substackcdn.com/image/fetch/$s_!-9Z8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d718fe8-d32a-4d13-825c-22911b16a496_716x424.png 848w, https://substackcdn.com/image/fetch/$s_!-9Z8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d718fe8-d32a-4d13-825c-22911b16a496_716x424.png 1272w, https://substackcdn.com/image/fetch/$s_!-9Z8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d718fe8-d32a-4d13-825c-22911b16a496_716x424.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 1.6: Concept diagram of Platform, Framework, SDK, Library, and API</em></p><ul><li><p>Platforms are the most complete and mature kind of reusable engineering elements; however, their value comes with a high cost of ownership &#8211; this means that only large-scale companies can afford to build and operate an internal software development platform. Please do not confuse internal SDP with commercial PaaS (platform as a service) &#8211; we have many examples that successful PaaS can be created and operated with relatively small investment.</p></li></ul><h2>Domain Specific Language</h2><p>Here it is, the bonus addition to the chapter. A <strong>Domain Specific Language</strong> (<strong>DSL</strong>) is a &#8220;language&#8221; with a higher level of abstraction optimized for a specific class of problems. A DSL uses the concepts and rules from the field or domain.</p><blockquote><p><strong>Note</strong></p><p>Please note that DSL is not a programming language but rather a &#8220;formal domain description&#8221; language. In most cases, DSLs implementations are closer to executable configuration in JSON, YAML, XML or similar formats.</p></blockquote><p>In terms of architecture abstractions, extensibility, and control flow, DSL is very similar to ADF &#8211; they both imply certain design patterns, employ libraries for extensibility and portability purposes, and invoke necessary code in the right time, defined by DSL creators.</p><p>The difference is the level of freedom for software product developers to code the DSL execution. We may consider DSL a &#8220;next level&#8221; framework suitable for cases when we want to achieve a high grade of standardization at a high level of abstraction.</p><p>Typical simplified taxonomy of the DSLs is the following:</p><ul><li><p>Workflow/BPMN like Camunda, Oracle BPMS, Nikku</p></li><li><p>Rules like Drools</p></li><li><p>Infra like Terraform</p></li><li><p>Development like Gradle</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FOOQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae80a8d-5170-4b6b-9db3-2cb7dfcccb0f_716x525.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FOOQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae80a8d-5170-4b6b-9db3-2cb7dfcccb0f_716x525.png 424w, https://substackcdn.com/image/fetch/$s_!FOOQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae80a8d-5170-4b6b-9db3-2cb7dfcccb0f_716x525.png 848w, https://substackcdn.com/image/fetch/$s_!FOOQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae80a8d-5170-4b6b-9db3-2cb7dfcccb0f_716x525.png 1272w, https://substackcdn.com/image/fetch/$s_!FOOQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae80a8d-5170-4b6b-9db3-2cb7dfcccb0f_716x525.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FOOQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae80a8d-5170-4b6b-9db3-2cb7dfcccb0f_716x525.png" width="716" height="525" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bae80a8d-5170-4b6b-9db3-2cb7dfcccb0f_716x525.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:525,&quot;width&quot;:716,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 1.10: Concept diagram with DSL, Platform, Framework, SDK, Library, and API&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 1.10: Concept diagram with DSL, Platform, Framework, SDK, Library, and API" title="Figure 1.10: Concept diagram with DSL, Platform, Framework, SDK, Library, and API" srcset="https://substackcdn.com/image/fetch/$s_!FOOQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae80a8d-5170-4b6b-9db3-2cb7dfcccb0f_716x525.png 424w, https://substackcdn.com/image/fetch/$s_!FOOQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae80a8d-5170-4b6b-9db3-2cb7dfcccb0f_716x525.png 848w, https://substackcdn.com/image/fetch/$s_!FOOQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae80a8d-5170-4b6b-9db3-2cb7dfcccb0f_716x525.png 1272w, https://substackcdn.com/image/fetch/$s_!FOOQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae80a8d-5170-4b6b-9db3-2cb7dfcccb0f_716x525.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Figure 1.7: Concept diagram with DSL, Platform, Framework, SDK, Library, and API</p><p>The diagram demonstrates a DSL primary use case of extending a Framework with limited predefined capabilities.</p><p>In this section we focused on key differentiators of Frameworks in comparison with other kinds of &#8220;engineering building blocks&#8221;. Now we can concentrate on the most important aspects of Frameworks, including the ones we further discuss in the next chapters.</p><h1>Differentiating ADF and other Types of Frameworks</h1><p>There are even more sources of confusion: we have Frameworks that could be used in the process of developing software products, but these Frameworks are not ADFs!</p><p>In the realm of software development, <strong>framework</strong> is a broad term encompassing various tools that structure and streamline different aspects of the process. We&#8217;ve established that Application Development Frameworks (ADFs) directly assist with coding. Let&#8217;s delve deeper into two other crucial categories: <strong>Architecture Frameworks </strong>and <strong>Software Delivery Frameworks</strong>.</p><p>&#8230;</p><h2>Architecture Frameworks</h2><p>These frameworks provide a structured approach to designing the overall architecture of a software system. Think of them as <em>organizational architecture design process</em> blueprints or roadmaps that define the foundation upon which your application will be built.</p><p>Here&#8217;s a breakdown of Architecture Frameworks&#8217; types:</p><blockquote><p><strong>Enterprise Architecture Frameworks</strong> (<strong>EAFs</strong>): Focuses on the high-level structure of an entire organization&#8217;s IT infrastructure, including software applications, data, and hardware. Examples: TOGAF, Zachman Framework.</p></blockquote><blockquote><p><strong>Software Architecture Frameworks</strong> (<strong>SAFs</strong>): Specializes in designing the internal structure of a single software application. Examples: 4+1 View Model, C4 Model.</p></blockquote><p>The following are some of the benefits of adopting such frameworks:</p><blockquote><ul><li><p><strong>Consistency</strong>: Promotes a standardized approach to design, ensuring all components fit together seamlessly.</p></li><li><p><strong>Communication</strong>: Provide a common language for stakeholders (architects, developers, etc.) to discuss system design.</p></li><li><p><strong>Reduced Complexity</strong>: Break down complex systems into manageable components, simplifying design and development.</p></li></ul></blockquote><p>The following are some of the popular examples of the architecture frameworks:</p><ul><li><p><strong><a href="http://TOGAF">TOGAF</a></strong> (<strong>The Open Group Architecture Framework</strong>): A widely used EAF known for its comprehensive approach to enterprise architecture.</p></li><li><p><strong><a href="https://zachman-feac.com/zachman/about-the-zachman-framework">Zachman Framework:</a></strong> Another EAF, offering a framework for classifying architectural information across different viewpoints (e.g., business, data, application).</p></li><li><p><strong><a href="https://arxiv.org/pdf/2006.04975">4+1 View Model</a></strong>: A SAF focusing on five architectural viewpoints (system, application, deployment, container, and code) for designing software applications.</p></li></ul><p>(Another example is <a href="https://dodcio.defense.gov/Library/DoD-Architecture-Framework/">DoDAF</a>)</p><p>Architecture Frameworks can be very useful, but they are completely out of scope of this book.</p><h2>Software Delivery Frameworks</h2><p>These frameworks focus on streamlining the entire software development and delivery process, particularly for large-scale or complex projects. They don&#8217;t deal with the specifics of coding or designing the application itself, but rather how to efficiently manage the development lifecycle.</p><p>Here&#8217;s a closer look at <strong>Software Delivery Frameworks</strong>:</p><p><strong>Core Principles</strong>: Emphasize iterative development, continuous integration and continuous delivery (CI/CD), and agile methodologies.</p><p>The following are some of the benefits:</p><ul><li><p><strong>Improved Efficiency</strong>: Streamlines workflows and processes to deliver software faster and with fewer errors.</p></li><li><p><strong>Enhanced Communication</strong>: Fosters collaboration between development teams, product managers, and stakeholders.</p></li><li><p>Increased Adaptability: Enables teams to respond to changing requirements and market needs more effectively.</p></li></ul><p>The following are some of the popular examples:</p><ul><li><p><strong><a href="https://framework.scaledagile.com/">Scaled Agile Framework (SAFe)</a></strong>: A popular framework for scaling agile methodologies to large enterprises.</p></li><li><p><strong><a href="http://Large Scale Scrum (LeSS)">Large Scale Scrum</a></strong><a href="http://Large Scale Scrum (LeSS)"> (</a><strong><a href="http://Large Scale Scrum (LeSS)">LeSS</a></strong><a href="http://Large Scale Scrum (LeSS)">)</a>: An adaptation of the Scrum framework designed for large teams working on complex projects.</p></li><li><p><strong><a href="https://www.pmi.org/disciplined-agile/process/introduction-to-dad">Disciplined Agile Delivery (DAD)</a></strong>: A framework that integrates various agile practices with other project management methodologies.</p></li></ul><p>These categories provide a glimpse into the diverse landscape of frameworks beyond ADFs. Architecture Frameworks ensure a well-designed foundation for your software system, while Software Delivery Frameworks guide the overall development journey with efficiency and agility. By understanding and leveraging these frameworks, you can build robust and successful software applications.</p><p>But as we decided to focus on ADF, we need to pay special attention to a Software Development Lifecycle topic as it covers the key value of using application development frameworks.</p><h1>Software Development Lifecycle (SDLC) and Flow</h1><p>To better understand Application Development Frameworks, we need first to understand key scenarios of ADF adoption.</p><p>To set up a context, we need to differentiate the design-time and operations (runtime) aspects of the software product development. It is important to note that the <strong>Software Development Lifecycle</strong> (<strong>SDLC</strong>) can be used as a &#8220;blueprint&#8221; for software product development iteration. Visual diagram of SDLC can help us better understand this aspect:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!akyi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F354acbdf-1a65-44b6-937f-c69d506447c9_665x719.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!akyi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F354acbdf-1a65-44b6-937f-c69d506447c9_665x719.png 424w, https://substackcdn.com/image/fetch/$s_!akyi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F354acbdf-1a65-44b6-937f-c69d506447c9_665x719.png 848w, https://substackcdn.com/image/fetch/$s_!akyi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F354acbdf-1a65-44b6-937f-c69d506447c9_665x719.png 1272w, https://substackcdn.com/image/fetch/$s_!akyi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F354acbdf-1a65-44b6-937f-c69d506447c9_665x719.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!akyi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F354acbdf-1a65-44b6-937f-c69d506447c9_665x719.png" width="665" height="719" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/354acbdf-1a65-44b6-937f-c69d506447c9_665x719.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:719,&quot;width&quot;:665,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 1.11: Spiral SDLC model is one of the most advanced SDLC models for software development: image credits to https://www.tutorialspoint.com/sdlc/sdlc_spiral_model.htm&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 1.11: Spiral SDLC model is one of the most advanced SDLC models for software development: image credits to https://www.tutorialspoint.com/sdlc/sdlc_spiral_model.htm" title="Figure 1.11: Spiral SDLC model is one of the most advanced SDLC models for software development: image credits to https://www.tutorialspoint.com/sdlc/sdlc_spiral_model.htm" srcset="https://substackcdn.com/image/fetch/$s_!akyi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F354acbdf-1a65-44b6-937f-c69d506447c9_665x719.png 424w, https://substackcdn.com/image/fetch/$s_!akyi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F354acbdf-1a65-44b6-937f-c69d506447c9_665x719.png 848w, https://substackcdn.com/image/fetch/$s_!akyi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F354acbdf-1a65-44b6-937f-c69d506447c9_665x719.png 1272w, https://substackcdn.com/image/fetch/$s_!akyi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F354acbdf-1a65-44b6-937f-c69d506447c9_665x719.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 1.8: Spiral SDLC model is one of the most advanced SDLC models for software development: image credits to <a href="https://www.tutorialspoint.com/sdlc/sdlc_spiral_model.htm">tutorialspoint</a></em></p><p>We should always assume a repeatable iterative nature of the software product development process not because &#8220;agile&#8221; is our current state-of-the-art delivery methodology; every lean or efficient delivery approach relies on feedback loops to enable continuous improvement.</p><p>While ADF can be defined from both design-time and operations (runtime) perspectives, its primary value is always in design-time and SDLC. We can impact routines as repeatable and reproducible tasks. That&#8217;s how it is different from software libraries or APIs that can be employed for a one-time task that might never be repeated again (e.g. library that provides integration to specialized hardware or vendor-locked API that can be used in multiple places in source code, but the use of a library is not about repeatable part of SDLC task).</p><p>In systems engineering terminology we can declare that SDLC is a &#8220;using system&#8221; (or a supersystem) for ADF, and ADF is a &#8220;subsystem&#8221; of SDLC. This implies not only the fact that ADF is literally part of SDLC, but also enables the following mental model:</p><ul><li><p>ADF value, success criteria, and metrics are targeting SDLC improvements</p></li><li><p>ADF stakeholders&#8217; roles are SDLC participants</p></li><li><p>Key ADF architecture viewpoints are defined in SDLC</p></li></ul><p>In addition to SDLC concept, which is focused on engineering and instrumenting the development process, we can apply a &#8220;Flow&#8221; term to emphasize the systemic aspect of software product development in terms of value throughput and delivery management.</p><blockquote><p><strong>ADF and Flow</strong></p><p>&#8230;ADF (is) s one of the most influencing ways to optimize a Flow throughput and eliminate bottlenecks related to product and process complexity.</p></blockquote><h1>Summary</h1><p>An Application Development Framework is a software product &#8220;skeleton&#8221; that offers a fundamental structure for building applications within a specific environment. It acts as a reusable foundation, supplying pre-defined functionalities and promoting code organization through established conventions. This distinguishes it from other ways of reusing code in the form of an SDK, library, or API. It also makes ADF a unique opportunity to streamline development by reducing repetitive coding efforts, decreasing cognitive load, and promoting architecture best practices.</p><p>With this knowledge, we can explore the next chapters to find a way to calculate ADF return on investment, meet stakeholders&#8217; expectations, and align the ADF roadmap with a common maturity model.</p><h1>Further reading</h1><p>To know more please visit the (following) links:</p><ul><li><p><em><a href="https://developer.android.com/studio">Vendor-specific hardware SDKs like Android Studio</a></em></p></li><li><p><em><a href="https://developer.samsung.com/smarttv/develop/getting-started/setting-up-sdk/installing-tv-sdk.html">Samsung TV SDK</a></em></p></li><li><p><em><a href="https://learn.microsoft.com/en-us/gaming/gdk/docs/gdk-dev/console-dev/dev-kits/devkit-contents">MS Xbox</a></em></p></li><li><p><em><a href="https://developers.pandadoc.com/reference/sdk">Product-specific SDKs like PandaDoc SDK</a></em></p></li><li><p><em>Hubspot SDK:</em> <a href="https://developers.hubspot.com/docs/platform/ui-extensions-sdk">https://developers.hubspot.com/docs/platform/ui-extensions-sdk</a></p></li><li><p><em><a href="https://getstream.io/chat/sdk/ios/">GetStream</a></em></p></li><li><p><em>Platform SDKs like AWS</em>: <a href="https://aws.amazon.com/chime/chime-sdk/">Amazon Chime SDK</a> &amp; <a href="https://aws.amazon.com/sdk-for-net/">AWS SDK for .NET</a></p></li><li><p><em><a href="https://github.com/Azure/azure-sdk">Azure</a> or Google&#8217;s <a href="https://cloud.google.com/sdk">Cloud SDK</a></em></p></li></ul><div><hr></div><p>If you found this chapter insightful, check out <em><strong><a href="https://www.packtpub.com/en-us/product/building-an-application-development-framework-9781836208570">Building an Application Development Framework</a></strong></em> by <strong>Ivan Padabed</strong> and <strong>Roman Voronin</strong> (Packt, Sept 2025). It&#8217;s a practical, end-to-end playbook for designing, building, and rolling out a custom ADF that measurably improves flow and quality across your SDLC. The first edition includes real-world cases and a downloadable code bundle.</p><p>You&#8217;ll learn to: design and implement a robust ADF architecture; choose technologies for performance and scalability; manage versioning and packaging for efficient distribution; harden the framework&#8217;s security posture; foster collaboration to build an extensible ADF ecosystem; and measure and optimize performance (including DORA-style outcomes) for continuous improvement. Ideal for technical leaders, engineering managers, and developers who want a pragmatic, code-backed path to a reusable framework that scales with their organization.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.packtpub.com/en-us/product/building-an-application-development-framework-9781836208570" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!whlm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa807bff8-4b44-4477-8828-fcec5f73cfe3_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!whlm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa807bff8-4b44-4477-8828-fcec5f73cfe3_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!whlm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa807bff8-4b44-4477-8828-fcec5f73cfe3_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!whlm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa807bff8-4b44-4477-8828-fcec5f73cfe3_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!whlm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa807bff8-4b44-4477-8828-fcec5f73cfe3_2250x2775" width="364" height="449" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a807bff8-4b44-4477-8828-fcec5f73cfe3_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:364,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Building an Application Development Framework&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.packtpub.com/en-us/product/building-an-application-development-framework-9781836208570&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Building an Application Development Framework" title="Building an Application Development Framework" srcset="https://substackcdn.com/image/fetch/$s_!whlm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa807bff8-4b44-4477-8828-fcec5f73cfe3_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!whlm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa807bff8-4b44-4477-8828-fcec5f73cfe3_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!whlm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa807bff8-4b44-4477-8828-fcec5f73cfe3_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!whlm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa807bff8-4b44-4477-8828-fcec5f73cfe3_2250x2775 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here&#8217;s what some readers have said:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SE9K!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd05c8a-1193-4e58-adee-ecaeb4df18cc_865x442.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SE9K!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd05c8a-1193-4e58-adee-ecaeb4df18cc_865x442.png 424w, https://substackcdn.com/image/fetch/$s_!SE9K!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd05c8a-1193-4e58-adee-ecaeb4df18cc_865x442.png 848w, https://substackcdn.com/image/fetch/$s_!SE9K!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd05c8a-1193-4e58-adee-ecaeb4df18cc_865x442.png 1272w, https://substackcdn.com/image/fetch/$s_!SE9K!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd05c8a-1193-4e58-adee-ecaeb4df18cc_865x442.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SE9K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd05c8a-1193-4e58-adee-ecaeb4df18cc_865x442.png" width="865" height="442" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3cd05c8a-1193-4e58-adee-ecaeb4df18cc_865x442.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:442,&quot;width&quot;:865,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:104029,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/176895118?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd05c8a-1193-4e58-adee-ecaeb4df18cc_865x442.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SE9K!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd05c8a-1193-4e58-adee-ecaeb4df18cc_865x442.png 424w, https://substackcdn.com/image/fetch/$s_!SE9K!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd05c8a-1193-4e58-adee-ecaeb4df18cc_865x442.png 848w, https://substackcdn.com/image/fetch/$s_!SE9K!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd05c8a-1193-4e58-adee-ecaeb4df18cc_865x442.png 1272w, https://substackcdn.com/image/fetch/$s_!SE9K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cd05c8a-1193-4e58-adee-ecaeb4df18cc_865x442.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lxMu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a854ab-a018-4179-b86e-bc1f3516345c_866x187.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lxMu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a854ab-a018-4179-b86e-bc1f3516345c_866x187.png 424w, https://substackcdn.com/image/fetch/$s_!lxMu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a854ab-a018-4179-b86e-bc1f3516345c_866x187.png 848w, https://substackcdn.com/image/fetch/$s_!lxMu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a854ab-a018-4179-b86e-bc1f3516345c_866x187.png 1272w, https://substackcdn.com/image/fetch/$s_!lxMu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a854ab-a018-4179-b86e-bc1f3516345c_866x187.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lxMu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a854ab-a018-4179-b86e-bc1f3516345c_866x187.png" width="866" height="187" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/89a854ab-a018-4179-b86e-bc1f3516345c_866x187.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:187,&quot;width&quot;:866,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:34554,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/176895118?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a854ab-a018-4179-b86e-bc1f3516345c_866x187.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lxMu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a854ab-a018-4179-b86e-bc1f3516345c_866x187.png 424w, https://substackcdn.com/image/fetch/$s_!lxMu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a854ab-a018-4179-b86e-bc1f3516345c_866x187.png 848w, https://substackcdn.com/image/fetch/$s_!lxMu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a854ab-a018-4179-b86e-bc1f3516345c_866x187.png 1272w, https://substackcdn.com/image/fetch/$s_!lxMu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a854ab-a018-4179-b86e-bc1f3516345c_866x187.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p>]]></content:encoded></item><item><title><![CDATA[From Prototype to Production: Why AI Systems Need Architecture Discipline]]></title><description><![CDATA[How architecture makes model- and data-driven systems production-ready]]></description><link>https://deepengineering.substack.com/p/from-prototype-to-production-why</link><guid isPermaLink="false">https://deepengineering.substack.com/p/from-prototype-to-production-why</guid><dc:creator><![CDATA[Richard D Avila]]></dc:creator><pubDate>Thu, 16 Oct 2025 05:25:33 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/972fef15-a47e-4c53-be78-af42a62be545_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a href="https://mlq.ai/media/quarterly_decks/v0.1_State_of_AI_in_Business_2025_Report.pdf">The GenAI Divide: STATE OF AI IN BUSINESS 2025</a> study by MIT NANDA surveyed several hundred executives on AI initiatives and found that only <strong>5%</strong> of AI projects demonstrated lasting value. AI initiatives fail to make it to production because teams don&#8217;t define how the AI will deliver value or account for the complex realities of a production environment. In this article, we discuss specific gates and actions that, if followed, will mitigate the risk of a failed prototype-to-production deployment. A very short exemplar or mini-case study is included.</p><blockquote><p><strong>Why call out AI systems?</strong> Unlike traditional software, <strong>AI systems are model- and data-driven</strong>: behavior depends on training data, live inputs, and evaluation metrics. Architecture is how we make that behavior predictable and operable in production.</p></blockquote><h1>Why architecture work matters</h1><p>Architecture makes prototypes survivable in production by forcing end-to-end thinking about data reality, key non-functional constraints, stakeholder trust, and the documentation that turns design into testable behavior.</p><ul><li><p><strong>Data reality:</strong> The key driver in any AI system is the quality of the data used for training and inference. Data engineering is often not done well or not done with a large enough aperture to appreciate the full end-to-end system. For example: does the prototype assume well-formatted or highly structured data that is not possible in production? Did the prototype suppose that data into the system was ordered but, in a production environment, that is not the case? Was the system overly reliant on a technology that cannot scale to expected production data rates? And so on.</p></li><li><p><strong>Partitioning and organization:</strong> Software architecture involves design decisions about partitioning and organization. An analysis of the software components of the prototype can identify where choke points may occur, how interfaces into and out of the prototype were handled, and where a component may require more design work in a production environment.</p></li><li><p><strong>Non-functional requirements:</strong> A robust architecture captures and understands the key non-functional requirements at play in the production environment. For example, will the new AI functionality impact availability requirements that, if not met, would result in a critical error? Does the new model with higher production data rates impact performance requirements that have cascading effects?</p></li><li><p><strong>Trust tolerance:</strong> Robust architecting should illuminate the trust tolerance and decision making of key stakeholders. For example, does a single off-nominal inference or decision result in a loss of confidence or outright hostility to the new AI functionality? As part of a conceptual design, guard rails or off ramps can be put into the design so there is a sort of &#8220;no-harm&#8221; guarantee that may cause consternation but not a loss of confidence.</p></li><li><p><strong>Documentation to test:</strong> The documentation of a conceptual design can aid in defining and illuminating test cases and scenarios that the prototype must satisfy in a production environment.</p></li></ul><h2>A fair criticism&#8212;and the response</h2><blockquote><p>It&#8217;s reasonable to say that architecting adds &#8220;document-centric&#8221; work that slows delivery. But the MIT statistic exists for a reason: <strong>no amount of heroic coding fixes a flawed architecture</strong>. Water doesn&#8217;t flow uphill; a bad architecture produces a bad system. Agile methods work best when a <strong>stable architectural frame</strong> guides rapid prototyping, integration, and demos. Productive engineering isn&#8217;t only writing code&#8212;it&#8217;s also designing systems that can survive production.</p></blockquote><p><strong>Architecting</strong> is critical in guiding engineering decisions and priorities. This is done by laying out the conceptual design of the end system. One of the first drivers of architecture is to have a solid conceptual design that informs prototyping. That same design later anchors the pass/fail criteria that we can categorize into gates.</p><h1>Five production-readiness gates (with actions)</h1><p>There are five gates or decision buckets&#8212;pass/fail outcomes we must clear before go-live.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gmIM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0195efce-b9d1-4bcd-8298-d91856eca0fb_1024x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gmIM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0195efce-b9d1-4bcd-8298-d91856eca0fb_1024x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!gmIM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0195efce-b9d1-4bcd-8298-d91856eca0fb_1024x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!gmIM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0195efce-b9d1-4bcd-8298-d91856eca0fb_1024x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!gmIM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0195efce-b9d1-4bcd-8298-d91856eca0fb_1024x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gmIM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0195efce-b9d1-4bcd-8298-d91856eca0fb_1024x768.jpeg" width="622" height="466.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0195efce-b9d1-4bcd-8298-d91856eca0fb_1024x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1024,&quot;resizeWidth&quot;:622,&quot;bytes&quot;:69265,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/176209001?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0195efce-b9d1-4bcd-8298-d91856eca0fb_1024x768.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gmIM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0195efce-b9d1-4bcd-8298-d91856eca0fb_1024x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!gmIM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0195efce-b9d1-4bcd-8298-d91856eca0fb_1024x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!gmIM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0195efce-b9d1-4bcd-8298-d91856eca0fb_1024x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!gmIM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0195efce-b9d1-4bcd-8298-d91856eca0fb_1024x768.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p>Note: the following actions are not exhaustive.</p></blockquote><h2>1) Data Engineering <em>(AI inputs and contracts)</em></h2><ul><li><p><strong>Analysis:</strong> Confirm that production inputs and interfaces match what the model expects&#8212;data rates, formats, fields, identifiers, consistency, and summary statistics.</p></li><li><p><strong>Ingest testing:</strong> Exercise the pipeline with <em>current</em> production data and verify ingest correctness and throughput.</p></li><li><p><strong>Resilience checks:</strong> Prove the prototype tolerates malformed records, off-nominal flows, and misformatted streams without cascading failures.</p></li></ul><h3>2) Model Correctness <em>(does the model still earn its keep?)</em></h3><ul><li><p><strong>Ongoing relevance:</strong> Verify the model still solves a real user problem; retire it if it no longer does.</p></li><li><p><strong>Performance on live data:</strong> Re-evaluate with current production samples; confirm metrics such as AUC, accuracy, and false positive rate meet or exceed targets.</p></li><li><p><strong>Execution time:</strong> Show end-to-end model latency remains within bounds required by downstream consumers and users.</p></li></ul><h3>3) Observability <em>(for data pipelines and model behavior)</em></h3><ul><li><p><strong>Data flow telemetry:</strong> Logging and monitors for inputs, transformations, and handoffs.</p></li><li><p><strong>Model execution telemetry:</strong> Traces, metrics, and logs for inference paths and failure modes.</p></li><li><p><strong>Acceptance bounds:</strong> Document thresholds, alert rules, and expected distributions for observability metrics.</p></li></ul><h3>4) Operations <em>(release and recovery for AI pipelines)</em></h3><ul><li><p><strong>Canaries:</strong> Implement canary or shadow releases to surface pipeline issues before full rollout.</p></li><li><p><strong>Time budgets:</strong> Define and monitor time bounds for the full pipeline and each critical stage.</p></li><li><p><strong>Update paths:</strong> Document steps for model refresh, rollback, and disentanglement from production if needed.</p></li></ul><h3>5) Data Governance <em>(quality, compliance, ethics)</em></h3><ul><li><p><strong>Quality audit:</strong> Run a data quality review on the latest production datasets.</p></li><li><p><strong>Compliance docs:</strong> Prepare artifacts to satisfy regulatory, security, and privacy requirements.</p></li><li><p><strong>Ethical filters:</strong> Enforce policy-aligned filtering and access controls.</p></li></ul><blockquote><p>In addition to these gates, the key non-functional requirements for the system need to be monitored, with action plans to ensure they are met&#8212;for example, time budgets across the system via specialized logging and canary deployments.</p></blockquote><p>The gates define what must be true to go live; let us go a step further and look at the rubric that translates the actions into concrete checks and artifacts that prove each gate is passed.</p><h1>Rubric for pre-deployment checks</h1><p>Here is a rubric you can use as a go/no-go gate: confirm business relevance, data contracts, operational readiness (including rollback), on-call coverage and monitoring, and model quality against explicit SLOs before promoting any prototype to production. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lwu4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adeb70e-f069-4726-905f-609558ffdae4_1024x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lwu4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adeb70e-f069-4726-905f-609558ffdae4_1024x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!lwu4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adeb70e-f069-4726-905f-609558ffdae4_1024x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!lwu4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adeb70e-f069-4726-905f-609558ffdae4_1024x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!lwu4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adeb70e-f069-4726-905f-609558ffdae4_1024x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lwu4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adeb70e-f069-4726-905f-609558ffdae4_1024x768.jpeg" width="664" height="498" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8adeb70e-f069-4726-905f-609558ffdae4_1024x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1024,&quot;resizeWidth&quot;:664,&quot;bytes&quot;:129887,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/176209001?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adeb70e-f069-4726-905f-609558ffdae4_1024x768.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lwu4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adeb70e-f069-4726-905f-609558ffdae4_1024x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!lwu4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adeb70e-f069-4726-905f-609558ffdae4_1024x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!lwu4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adeb70e-f069-4726-905f-609558ffdae4_1024x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!lwu4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8adeb70e-f069-4726-905f-609558ffdae4_1024x768.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This rubric is the evidence and checks that prove each gate is passed and that you can go live. </p><ul><li><p><strong>Business Checks</strong></p><ul><li><p>Is the model output still relevant to intended users?</p></li><li><p>Is the prototype operating at time scales relevant to users?</p></li><li><p>Is the prototype still within budget and resource limits?</p></li></ul></li><li><p><strong>Data Engineering</strong></p><ul><li><p>Verify data sources.</p></li><li><p>Verify interfaces still meet service-level agreements (format, content, time budgets).</p></li><li><p>Confirm computational resources for scale are in place.</p></li></ul></li><li><p><strong>Operations</strong></p><ul><li><p>Verify the prototype in a pre-production environment or a simulation that mimics true production.</p></li><li><p>Confirm datastore schemas and queries execute correctly and produce expected results.</p></li><li><p>Ensure error logging exists at multiple points with sufficient detail for troubleshooting.</p></li><li><p>Identify a rollback procedure to take the model offline if needed.</p></li></ul></li><li><p><strong>Pre-deployment</strong></p><ul><li><p>Identify a developer or staff resource to support rollout and address issues promptly.</p></li><li><p>Ensure monitoring is in place to capture operations.</p></li></ul></li><li><p><strong>Model</strong></p><ul><li><p>Monitor non-functional requirements, SLAs, and errors.</p></li><li><p>Track correctness metrics (AUC, accuracy) and model drift.</p></li><li><p>Provide a user interface (or tools) to visualize execution.</p></li><li><p>Supply guides/documentation for troubleshooting, architecture diagrams, expected outputs at each stage, and accessible source code.</p></li><li><p>Use canaries with injected known datasets for monitoring.</p></li></ul></li></ul><h1>Mini case study</h1><p>To understand how the 5 gates and the rubric work in cohesion, let us take the example of a new application that recommends technical articles to an engineering audience. Before launch, the team decides to apply actions under each of the <strong>5 gates</strong> and the <strong>Pre-deployment checks </strong>reveal the following:</p><ul><li><p><strong>Data &amp; Schema</strong></p><ul><li><p>Since training began, an extra tag field was added; the <strong>date format also changed</strong>, breaking metadata parsing.</p></li><li><p>The <strong>datastore schema drifted</strong>: a key field changed, requiring interface updates.</p></li></ul></li><li><p><strong>Model Correctness: </strong>On current production data, <strong>AUC dropped below training thresholds</strong>; investigation showed an <strong>over-represented training subset</strong>.</p></li><li><p><strong>Observability: Logging failed</strong>; output and processed-file logs couldn&#8217;t be written due to <strong>permission issues</strong>.</p></li><li><p><strong>Operations &amp; Release: </strong>The team planned to launch at <strong>peak traffic</strong> with <strong>no rollback procedure</strong>.</p></li><li><p><strong>Governance: </strong>A compliance check found <strong>personal information</strong> was being collected and <strong>shouldn&#8217;t have been</strong>.</p></li></ul><p>As a result, a failed release is prevented.</p><p>For AI systems, some architecting during prototyping helps cross the chasm from an impressive demonstration to a production system that delivers true value and positive business outcomes.</p><div><hr></div><p>If you found this article insightful, <strong>Richard D Avila</strong> and <strong>Imran Ahmad&#8217;s</strong> forthcoming book, <strong><a href="https://www.packtpub.com/en-us/product/architecting-ai-software-systems-9781804619469">Architecting AI Software Systems</a></strong> extends the same ideas with a structured approach to integrating AI into traditional architectures&#8212;covering how inference and decision-making shape design, using architectural models to ensure cohesion, mitigating risks like underperformance and cost overruns, and applying patterns and heuristics for scalable, high-performance systems&#8212;anchored by real-world case studies, hands-on exercises, and a complete example of an AI-enabled system for architects, engineering leaders, and AI/ML practitioners.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.packtpub.com/en-us/product/architecting-ai-software-systems-9781804619469" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!y8iB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29a0790d-4648-401b-a181-4c4d549a909d_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!y8iB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29a0790d-4648-401b-a181-4c4d549a909d_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!y8iB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29a0790d-4648-401b-a181-4c4d549a909d_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!y8iB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29a0790d-4648-401b-a181-4c4d549a909d_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!y8iB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29a0790d-4648-401b-a181-4c4d549a909d_2250x2775" width="346" height="426.7967032967033" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/29a0790d-4648-401b-a181-4c4d549a909d_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:346,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Architecting AI Software Systems&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.packtpub.com/en-us/product/architecting-ai-software-systems-9781804619469&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Architecting AI Software Systems" title="Architecting AI Software Systems" srcset="https://substackcdn.com/image/fetch/$s_!y8iB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29a0790d-4648-401b-a181-4c4d549a909d_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!y8iB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29a0790d-4648-401b-a181-4c4d549a909d_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!y8iB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29a0790d-4648-401b-a181-4c4d549a909d_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!y8iB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29a0790d-4648-401b-a181-4c4d549a909d_2250x2775 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><p><strong>About the Author:</strong></p><p><strong>Richard D Avila</strong> is a software and systems architect with over two decades of industry experience building complex software systems. He has architected and held leadership roles for building a wide array of software systems from complex simulations, autonomy and data analytic systems. He is also a principal investigator for AI topics. He has published in referred journals and industry publications on command-and-control theory, assurance architectures, multi-agent modeling, and machine learning. He was the first expert instructor on data analytics at University of Maryland Baltimore County &#8211; Training Centers. Before working as a software and systems architect, he served in the US Navy as a submarine officer.</p>]]></content:encoded></item><item><title><![CDATA[Quantum Error Correction and Fault Tolerance]]></title><description><![CDATA[The complete &#8220;Chapter 14: Quantum Error Correction and Fault Tolerance&#8221; from the book A Practical Guide to Quantum Computing by El&#237;as F. Combarro and Samuel Gonz&#225;lez-Castillo (Packt, 2025)]]></description><link>https://deepengineering.substack.com/p/quantum-error-correction-and-fault</link><guid isPermaLink="false">https://deepengineering.substack.com/p/quantum-error-correction-and-fault</guid><dc:creator><![CDATA[Elías F. Combarro]]></dc:creator><pubDate>Thu, 02 Oct 2025 05:29:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!IBtK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99207264-fd08-4297-897b-3837b3b2f840_2250x2775" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>To preserve the original layout, equations, and figures, we&#8217;re providing this chapter as a PDF which you can download below.</p><div class="file-embed-wrapper" data-component-name="FileToDOM"><div class="file-embed-container-reader"><div class="file-embed-container-top"><image class="file-embed-thumbnail" src="https://substackcdn.com/image/fetch/$s_!J-2j!,w_400,h_600,c_fill,f_auto,q_auto:best,fl_progressive:steep,g_auto/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca9fafdf-b80f-4b70-9cdf-7fdf971f9a10_791x1024.jpeg"></image><div class="file-embed-details"><div class="file-embed-details-h1">The complete &#8220;Chapter 14: Quantum Error Correction and Fault Tolerance&#8221; from the book A Practical Guide to Quantum Computing by El&#237;as F. Combarro and Samuel Gonz&#225;lez-Castillo (Packt, 2025)</div><div class="file-embed-details-h2">4.55MB &#8729; PDF file</div></div><a class="file-embed-button wide" href="https://deepengineering.substack.com/api/v1/file/6d01b869-1ab7-45a7-a90e-11c970f25428.pdf"><span class="file-embed-button-text">Download</span></a></div><div class="file-embed-description">Chapter 14, Quantum Error Correction and Fault Tolerance, introduces quantum error correction, which could pave the road towards fault-tolerant computation and thus may be a cornerstone in the development of useful quantum computers. This chapter begins with a discussion on the necessity for quantum error correction, which is followed by the introduction and implementation of a simple quantum error-correcting code. The chapter finishes with some remarks on fault-tolerant computing and how quantum error correction can help enable it.</div><a class="file-embed-button narrow" href="https://deepengineering.substack.com/api/v1/file/6d01b869-1ab7-45a7-a90e-11c970f25428.pdf"><span class="file-embed-button-text">Download</span></a></div></div><div><hr></div><p>If you liked this chapter, check out <em><strong><a href="https://www.packtpub.com/en-in/product/a-practical-guide-to-quantum-computing-9781835885956">A Practical Guide to Quantum Computing: Hands-on approach to quantum computing with Qiskit</a></strong></em> by El&#237;as F. Combarro and Samuel Gonz&#225;lez-Castillo (Packt, July 2025). It&#8217;s a self-contained introduction that starts from a single qubit and builds to foundational algorithms&#8212;Deutsch, Bernstein&#8211;Vazirani, Grover, Shor&#8212;with runnable code in Qiskit 2.1 on simulators and real hardware. Along the way, it covers applications like quantum money and BB84, and introduces fault tolerance and the road to quantum advantage. </p><p>You&#8217;ll learn to: understand what makes a quantum computer unique; represent and measure multi-qubit states; use superposition, entanglement, and interference; implement and run quantum algorithms in Qiskit; explain how Grover&#8217;s search and Shor&#8217;s factoring work; and get familiar with quantum fault tolerance and emerging notions of quantum advantage. Ideal for university-level STEM courses and for professionals or self-learners who want a practical, code-first path into quantum computing.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IBtK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99207264-fd08-4297-897b-3837b3b2f840_2250x2775" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IBtK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99207264-fd08-4297-897b-3837b3b2f840_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!IBtK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99207264-fd08-4297-897b-3837b3b2f840_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!IBtK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99207264-fd08-4297-897b-3837b3b2f840_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!IBtK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99207264-fd08-4297-897b-3837b3b2f840_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IBtK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99207264-fd08-4297-897b-3837b3b2f840_2250x2775" width="348" height="429.2637362637363" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/99207264-fd08-4297-897b-3837b3b2f840_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:348,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A Practical Guide to Quantum Computing&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A Practical Guide to Quantum Computing" title="A Practical Guide to Quantum Computing" srcset="https://substackcdn.com/image/fetch/$s_!IBtK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99207264-fd08-4297-897b-3837b3b2f840_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!IBtK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99207264-fd08-4297-897b-3837b3b2f840_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!IBtK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99207264-fd08-4297-897b-3837b3b2f840_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!IBtK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99207264-fd08-4297-897b-3837b3b2f840_2250x2775 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here is what some readers have said:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6zNk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7cdae73-8538-4a5f-a5db-eaf54aaf42f1_853x758.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6zNk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7cdae73-8538-4a5f-a5db-eaf54aaf42f1_853x758.png 424w, https://substackcdn.com/image/fetch/$s_!6zNk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7cdae73-8538-4a5f-a5db-eaf54aaf42f1_853x758.png 848w, https://substackcdn.com/image/fetch/$s_!6zNk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7cdae73-8538-4a5f-a5db-eaf54aaf42f1_853x758.png 1272w, https://substackcdn.com/image/fetch/$s_!6zNk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7cdae73-8538-4a5f-a5db-eaf54aaf42f1_853x758.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6zNk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7cdae73-8538-4a5f-a5db-eaf54aaf42f1_853x758.png" width="853" height="758" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f7cdae73-8538-4a5f-a5db-eaf54aaf42f1_853x758.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:758,&quot;width&quot;:853,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:167282,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/175012810?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7cdae73-8538-4a5f-a5db-eaf54aaf42f1_853x758.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6zNk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7cdae73-8538-4a5f-a5db-eaf54aaf42f1_853x758.png 424w, https://substackcdn.com/image/fetch/$s_!6zNk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7cdae73-8538-4a5f-a5db-eaf54aaf42f1_853x758.png 848w, https://substackcdn.com/image/fetch/$s_!6zNk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7cdae73-8538-4a5f-a5db-eaf54aaf42f1_853x758.png 1272w, https://substackcdn.com/image/fetch/$s_!6zNk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7cdae73-8538-4a5f-a5db-eaf54aaf42f1_853x758.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pZ0d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64ea74-f674-43a2-9eba-f300daa37fdc_860x313.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pZ0d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64ea74-f674-43a2-9eba-f300daa37fdc_860x313.png 424w, https://substackcdn.com/image/fetch/$s_!pZ0d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64ea74-f674-43a2-9eba-f300daa37fdc_860x313.png 848w, https://substackcdn.com/image/fetch/$s_!pZ0d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64ea74-f674-43a2-9eba-f300daa37fdc_860x313.png 1272w, https://substackcdn.com/image/fetch/$s_!pZ0d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64ea74-f674-43a2-9eba-f300daa37fdc_860x313.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pZ0d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64ea74-f674-43a2-9eba-f300daa37fdc_860x313.png" width="860" height="313" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f64ea74-f674-43a2-9eba-f300daa37fdc_860x313.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:313,&quot;width&quot;:860,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:71542,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/175012810?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64ea74-f674-43a2-9eba-f300daa37fdc_860x313.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pZ0d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64ea74-f674-43a2-9eba-f300daa37fdc_860x313.png 424w, https://substackcdn.com/image/fetch/$s_!pZ0d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64ea74-f674-43a2-9eba-f300daa37fdc_860x313.png 848w, https://substackcdn.com/image/fetch/$s_!pZ0d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64ea74-f674-43a2-9eba-f300daa37fdc_860x313.png 1272w, https://substackcdn.com/image/fetch/$s_!pZ0d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64ea74-f674-43a2-9eba-f300daa37fdc_860x313.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p><a href="https://deepengineering.substack.com/api/v1/file/adef3bb7-2c9c-4998-b4f5-029c2d9b33e4.pdf">Download</a></p>]]></content:encoded></item><item><title><![CDATA[Clean Architecture Essentials: Transforming Python Development]]></title><description><![CDATA[The complete &#8220;Chapter 1: Clean Architecture Essentials: Transforming Python Development&#8221; from the book Clean Architecture with Python by Sam Keen (Packt, June 2025).]]></description><link>https://deepengineering.substack.com/p/clean-architecture-essentials-transforming</link><guid isPermaLink="false">https://deepengineering.substack.com/p/clean-architecture-essentials-transforming</guid><dc:creator><![CDATA[Sam Keen]]></dc:creator><pubDate>Wed, 17 Sep 2025 11:22:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!NtED!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7059d45c-81bb-4525-8a53-28d479a5593b_2250x2775" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As Python developers, we apply best practices such as writing clean functions, using descriptive variable names, and striving for modularity. Yet, as our applications grow, we often struggle to maintain this clarity and adaptability at scale. Python&#8217;s simplicity and versatility make it popular for projects ranging from web development to data science, but these strengths can become challenges as applications become more complex. We find ourselves lacking a master plan, an overarching architecture to guide our decisions and keep our projects maintainable as they evolve. This is where <strong>Clean Architecture</strong> comes into play, offering a structured approach to building Python applications that balance planning and agility, providing the architectural guidance we need for sustainable, large-scale development.</p><p>Clean Architecture, introduced by <a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">Robert C. Martin in 2012</a>, synthesizes decades of software design wisdom into a cohesive set of principles. It addresses persistent challenges in software development, such as managing complexity and accommodating change. By applying Clean Architecture principles to Python projects, developers can create systems that are not only functional but also maintainable, testable, and adaptable over time.</p><p>In this chapter, we&#8217;ll explore the essence of Clean Architecture and its relevance to Python development. We&#8217;ll examine how Clean Architecture principles align with Python&#8217;s philosophy of simplicity and readability, creating a natural synergy that enhances Python&#8217;s strengths. You&#8217;ll learn how Clean Architecture can help you build Python applications that are easier to understand, modify, and extend, even as they grow in complexity.</p><p>By the end of this chapter, you&#8217;ll have an overview of Clean Architecture principles and their potential benefits for Python development. You&#8217;ll be introduced to how this approach can address common challenges in software development, particularly as Python projects grow in scale and complexity. This foundational understanding of Clean Architecture will be essential as we delve deeper into its implementation and best practices in Python throughout the rest of the book.</p><p>In this chapter, we&#8217;re going to cover the following main topics:</p><ul><li><p>Why Clean Architecture in Python: the benefits of balancing planning and agility</p></li><li><p>What is Clean Architecture?</p></li><li><p>Clean Architecture and Python: a natural fit</p></li></ul><div><hr></div><h1>Why Clean Architecture in Python: the benefits of balancing planning and agility</h1><p>In this section, we&#8217;ll explore the critical balance between planning and agility in Python development and how Clean Architecture can help achieve this balance. We&#8217;ll examine the challenges posed by increasing complexity in modern Python applications and the imperative for agility in today&#8217;s fast-paced business environment. We&#8217;ll then discuss the trade-offs between planning and flexibility, and how architectural thinking can provide a framework for managing these trade-offs. Finally, we&#8217;ll look at the role of architecture in managing complexity and setting the stage for long-term success. Through these discussions, you&#8217;ll gain insight into why Clean Architecture is particularly valuable for Python developers striving to create maintainable, adaptable, and efficient applications.</p><p>Let&#8217;s begin by examining the complex challenges facing modern Python development.</p><h2>The complexity challenge in modern Python development</h2><p>As Python&#8217;s popularity soars, so do the scale and complexity of applications built with it. From web services to data science pipelines, Python projects are growing larger and more intricate. This growth brings significant challenges that every Python developer must grapple with.</p><p>The increasing complexity of systems makes them harder to understand, modify, and maintain. This complexity can severely limit your ability to add new features or respond to changing requirements. The maintenance burden of complex Python systems can overwhelm development teams, slowing down progress and innovation. Even small changes in large, complex systems can have far-reaching consequences, making modifications expensive and risky.</p><p>Consider a fictitious large Python-based e-commerce site: PyShop. The business decides to implement a seemingly simple feature: adding gift-wrapping options to orders. However, this straightforward addition quickly cascades into a complex project:</p><ul><li><p>The order processing module needs updates to include gift-wrapping choices</p></li><li><p>The inventory system requires modification to track gift-wrapping supplies</p></li><li><p>The pricing engine needs adjustments to calculate additional costs</p></li><li><p>The user interface (UI) must be updated to present gift-wrapping options</p></li><li><p>The fulfillment system needs changes to include gift-wrapping instructions</p></li></ul><p>What was estimated as a two-week-long task stretches into a multi-month project. Each change potentially impacts other system parts: adjustments in order processing affect reporting, inventory changes influence supply chain management, and UI modifications require extensive user experience testing.</p><p>This example highlights how interconnected modules in a complex system can turn a simple feature addition into a significant undertaking, emphasizing the need for an architecture that allows for more isolated changes and easier testing processes.</p><p>Moreover, as Python projects grow, developers often struggle with abstractions, a critical aspect that Clean Architecture helps address. Without proper guidance, codebases can suffer from extremes: either becoming a tangled mess of deeply nested class hierarchies that are hard to understand and modify or devolving into monolithic <em>do-everything</em> classes that lack any meaningful abstraction. In the former case, developers may create too-complex inheritance structures to maximize code reuse, resulting in a fragile system where changes in one place have unforeseen consequences elsewhere. In the latter case, the lack of abstraction leads to massive, unwieldy classes and rampant code duplication, making it nearly impossible to maintain consistency or make systemic changes. Both scenarios result in codebases that are difficult to understand, maintain, and extend, which is precisely the issues that a well-planned architecture helps prevent.</p><p>Furthermore, in today&#8217;s rapidly evolving tech landscape, complex, tightly coupled systems struggle to take advantage of new technologies. This limitation can significantly impact your ability to stay competitive in a field where technological agility is crucial.</p><h2>The agility imperative</h2><p>In our fast-paced business environment, agility is not just an advantage&#8212;it&#8217;s a necessity. With every company essentially becoming a technology company, the pressure to deliver quickly has never been higher. Python&#8217;s simplicity and extensive ecosystem make it an excellent choice for rapid development.</p><p>However, sustainable agility requires more than just initial speed, it demands architectural decisions that support ongoing evolution. It&#8217;s akin to building a high-performance race car: without proper design fundamentals, what starts as impressive acceleration quickly becomes limited by poor handling and maintenance challenges.</p><p>In rapidly evolving Python applications, this principle becomes starkly evident. Without a cohesive architecture, quick feature additions can create a tangled web of dependencies. What starts as a nimble codebase can, within months, become rigid and fragile. Developers find themselves spending more time deciphering existing code than writing new features. When it&#8217;s not immediately clear where new code should be added or how it should interact with existing components, developers under pressure may make hasty decisions, leading to suboptimal implementations and introducing bugs. These quick fixes further complicate the codebase, making future changes even more challenging. The initial velocity becomes unsustainable, not because of the speed itself, but due to the lack of a sturdy architectural foundation that can guide rapid changes and provide clear pathways for new feature integration.</p><p>Requirements change, often unpredictably. Your Python projects need to be structured in a way that allows for easy adaptation to these changes. This adaptability is crucial for long-term success in software development.</p><h2>Striking a balance: the planning&#8211;agility trade-off</h2><p>Finding the right balance between planning and agility is crucial in Python development. As Dave Thomas wisely said, &#8220;<em>Big design up front is dumb. Doing no design up front is even dumber.</em>&#8221; The key is finding the middle ground that allows for both structure and flexibility.</p><p>Good architecture helps you postpone decisions. It gives you the flexibility to push decisions to later stages when you have more information to make the correct choice. This approach is particularly valuable in Python development, where the language&#8217;s flexibility can sometimes lead to decision paralysis.</p><p>Introducing architectural thinking in Python development means considering the long-term structure of your project from the start, without over-engineering. It&#8217;s about creating a framework that guides development while remaining adaptable to change.</p><h2>The role of architecture in managing complexity</h2><p>Effective architecture is your best tool for managing complexity in Python systems. Good architecture simplifies complex systems by providing a clear structure and <strong>separation of concerns</strong> (<strong>SoC</strong>). One of the first steps in architecting a new system is determining how to divide it, keeping things that change for the same reason together and things that change for different reasons apart.</p><p>Consider two Python-based <strong>content management systems</strong> (<strong>CMSs</strong>) for media companies, both tasked with implementing a new AI-powered content tagging feature. In the well-architected system, this feature is implemented as a standalone module with clear interfaces. It integrates smoothly with the existing content creation and search modules through well-defined APIs. Developers can build and test the AI tagging service independently, and then connect it to the content database and UI with minimal disruption. Conversely, in a poorly structured system, adding this feature requires changes across the entire stack&#8212;from database schemas to frontend code&#8212;leading to unexpected bugs and performance issues. What takes a sprint in the well-architected system becomes a months-long refactoring project in the poorly structured one, demonstrating how thoughtful initial architecture can dramatically improve development efficiency and system adaptability.</p><p>The architectural decisions you make early on have a profound impact on the long-term development costs and flexibility of your Python projects. A well-architected system can significantly reduce the cost of change over time, allowing your team to respond more quickly to new requirements or technological changes.</p><h2>Preparing for Clean Architecture</h2><p>As we move toward discussing Clean Architecture, it&#8217;s important to understand that it offers a systematic approach to balancing planning and agility in Python projects. Architectural principles provide powerful tools for managing and reducing complexity in your Python systems.</p><p>At its core, Clean Architecture is about strategic SoC in your Python applications. It advocates for a structure where the essential business logic is insulated from external factors such as UIs, databases, and third-party integrations. This separation creates clear boundaries between different parts of your application, each with its own responsibilities. By doing so, Clean Architecture allows your core business rules to remain pure and unaffected by the implementation details of <strong>input/output</strong> (<strong>I/O</strong>) mechanisms or <strong>data management systems</strong> (<strong>DMSs</strong>).</p><p>By understanding these challenges and principles, you&#8217;ll be better prepared to appreciate the benefits that Clean Architecture can bring to your Python projects. In the next sections, we&#8217;ll delve into what Clean Architecture is and how it specifically applies to Python development, providing you with the tools to combat complexity and reduce the cost of change in your software systems.</p><div><hr></div><h1>What is Clean Architecture?</h1><p>Having explored the challenges of managing complexity in Python development and the need for balancing planning with agility, the goal of this section is to give you a high-level overview of Clean Architecture. We&#8217;ll be covering several key concepts and principles in quick succession to provide a broad understanding. Don&#8217;t worry if you don&#8217;t grasp every detail immediately. This is just the beginning of our journey. Each of these topics will be explored in depth in the chapters to come, where we&#8217;ll dive into practical Python implementations and real-world scenarios.</p><p>Clean Architecture synthesizes many ideas from previous architectural styles, but it is built around a fundamental concept: the separation of software elements into ring levels, with a strict rule that code dependencies can only move inward from outer levels. This principle is formally known as the <strong>Dependency Rule</strong>, one of the most critical aspects of Clean Architecture. The Dependency Rule states that source code dependencies must only point inward, toward higher-level policies. Inner circles must know nothing about outer circles, while outer circles must depend on and adapt to inner circles. This ensures that changes to external elements (like databases, UI, or frameworks) don&#8217;t impact the core business logic. The aim is to create software systems that are not only functional but also maintainable and adaptable over time. To illustrate this, let&#8217;s consider a simple Python application for a library management system:</p><ol><li><p>At the core, we have the <code>Book</code> class, representing the basic data structure.</p></li><li><p>Moving outward, we have a <code>BookInventory</code> class that manages operations on books.</p></li><li><p>In the outer ring, we have a <code>BookInterface</code> class that handles user interactions related to books.</p></li></ol><p>In this structure, the <code>Book</code> class knows nothing about the <code>BookInventory</code> or <code>BookInterface</code> classes. The <code>BookInventory</code> class might use the <code>Book</code> class but doesn&#8217;t know about the interface. This separation ensures that the core logic remains unaffected by external concerns.</p><p>Crucially, this structure allows us to modify or even replace outer layers without affecting the inner layers. For instance, we could change the UI from a <strong>command-line interface</strong> (<strong>CLI</strong>) to a web interface by modifying the <code>BookInterface</code> class, without needing to alter the <code>Book</code> or <code>BookInventory</code> classes. This flexibility is a key advantage of the Clean Architecture approach.</p><p>This structure is designed to produce systems that embody the key principles we introduced earlier:</p><ul><li><p>SoC</p></li><li><p>Independence of external details</p></li><li><p><strong>Testability</strong> and <strong>maintainability</strong></p></li></ul><p>Let&#8217;s explore further how Clean Architecture achieves these goals and the benefits it brings to software development.</p><h2>The onion architecture concept</h2><p>Let&#8217;s visualize the ring levels mentioned earlier and add another level of detail as to the purpose of each ring. Clean Architecture is often visualized as a series of concentric circles, like an onion. Each circle represents a different layer of software, and the Dependency Rule we discussed ensures that dependencies only flow inward across these boundaries. The core layers contain business logic (entities), while the external layers contain interface and implementation details (<em>see Figure 1.1</em>):</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6ExJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a3e611-5f6a-4b9d-be71-980ad4643b6e_1050x1050.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6ExJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a3e611-5f6a-4b9d-be71-980ad4643b6e_1050x1050.png 424w, https://substackcdn.com/image/fetch/$s_!6ExJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a3e611-5f6a-4b9d-be71-980ad4643b6e_1050x1050.png 848w, https://substackcdn.com/image/fetch/$s_!6ExJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a3e611-5f6a-4b9d-be71-980ad4643b6e_1050x1050.png 1272w, https://substackcdn.com/image/fetch/$s_!6ExJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a3e611-5f6a-4b9d-be71-980ad4643b6e_1050x1050.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6ExJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a3e611-5f6a-4b9d-be71-980ad4643b6e_1050x1050.png" width="1050" height="1050" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a2a3e611-5f6a-4b9d-be71-980ad4643b6e_1050x1050.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1050,&quot;width&quot;:1050,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 1.1: Clean Architecture: a series of concentric layers&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 1.1: Clean Architecture: a series of concentric layers" title="Figure 1.1: Clean Architecture: a series of concentric layers" srcset="https://substackcdn.com/image/fetch/$s_!6ExJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a3e611-5f6a-4b9d-be71-980ad4643b6e_1050x1050.png 424w, https://substackcdn.com/image/fetch/$s_!6ExJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a3e611-5f6a-4b9d-be71-980ad4643b6e_1050x1050.png 848w, https://substackcdn.com/image/fetch/$s_!6ExJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a3e611-5f6a-4b9d-be71-980ad4643b6e_1050x1050.png 1272w, https://substackcdn.com/image/fetch/$s_!6ExJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a3e611-5f6a-4b9d-be71-980ad4643b6e_1050x1050.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 1.1: Clean Architecture: a series of concentric layers</em></p><p><em>Figure 1.1</em> demonstrates the separation of inner core business logic progressing out to external interfaces:</p><ul><li><p><strong>Entities</strong>: At the center are entities, which encapsulate enterprise-wide business rules. Entities in this context are the primary <em>nouns</em> of your product, the core business objects that would exist even without software. For example, in an e-commerce system, entities might include <em>Customer</em>, <em>Product</em>, and <em>Order</em>. In a task management application, they could be <em>User</em>, <em>Task</em>, and <em>Project</em>. These entities contain the most basic, universal rules about how these objects behave and interact.</p></li><li><p><strong>Use Cases</strong>: The next layer contains use cases, which orchestrate the flow of data to and from entities. A use case represents a specific way the system is used. It&#8217;s essentially a description of how the system should behave for a particular scenario. For instance, in a task management app, use cases might include <em>Create New Task</em>, <em>Complete Task</em>, or <em>Assign Task</em>. Use cases contain application-specific business rules and control how and when entities are used to fulfill the goals of the application.</p></li><li><p><strong>Interface Adapters</strong>: Further out, we find interface adapters, which convert data between use cases and external agencies. This layer acts as a set of translators between the inner layers (entities and use cases) and the outer layer. It might include things such as controllers that handle HTTP requests, presenters that format data for display, and gateways that transform data for persistence. In a Python web application, this might include your view functions or classes that handle routing and request processing. A key point of this layer is that it allows us to decouple from frameworks.</p></li><li><p><strong>Frameworks and Drivers</strong>: The outermost layer contains frameworks and drivers, where the <em>external agencies</em> reside. By <em>drivers</em>, we mean the specific tools, frameworks, and delivery mechanisms that are used to run the system but aren&#8217;t core to the business logic. In a Python context, examples might include the following:</p><ul><li><p>Web frameworks such as Django or Flask</p></li><li><p>Database drivers such as <code>psycopg2</code> for PostgreSQL or <code>pymongo</code> for MongoDB</p></li><li><p>External libraries for tasks such as sending emails (for example, <code>smtplib</code>) or processing payments</p></li><li><p>UI frameworks if you&#8217;re building a desktop or mobile app (for example, PyQt)</p></li><li><p>System utilities for tasks such as logging or configuration management</p></li></ul></li></ul><p>This outermost layer is the most volatile, as it&#8217;s where we interact with the outside world and where technologies are most likely to change over time. By keeping it separate from our core business logic, we can more easily swap out these external tools without affecting the heart of our application.</p><p>This layered structure of Clean Architecture promotes SoC, establishing a clear organizational framework for software systems. Now that we have an idea of the fundamental structure of Clean Architecture, let&#8217;s further investigate its broader benefits.</p><h2>Benefits of Clean Architecture</h2><p>One of the primary advantages of Clean Architecture is its focus on protecting and isolating your core business logic, the domain objects that represent the foundation of your business. While external details such as web frameworks and persistence engines come and go, the true value to your business lies in the time invested in designing and implementing these core domain objects. Clean Architecture recognizes this and provides a structure that insulates these crucial components from the volatility of external technologies.</p><p>This architectural approach protects your investment in domain logic from the need to move away from a given framework or technology. For example, if a framework you&#8217;re using moves from an open source model to a proprietary one, Clean Architecture allows you to replace it without rewriting your core business logic. This separation significantly reduces the cost and risk of changes over time, allowing your system to evolve more easily as requirements change or as you need to adapt to new technologies. In essence, Clean Architecture ensures that the most valuable and stable part of your application, your business logic, remains unaffected by the often turbulent world of external technologies and frameworks.</p><p>Another key benefit is enhanced testability across all layers of the application. The independence of the core business logic from external details makes it much easier to write comprehensive unit tests. You can test business rules in isolation, without the need to spin up a database or web server or build cumbersome mocks. This leads to more thorough testing and, consequently, more robust software. It also encourages developers to write more tests, as the process becomes simpler and more straightforward.</p><p>Clean Architecture also provides flexibility in technology choices. Because the core of the application isn&#8217;t dependent on external frameworks or tools, you have the freedom to swap out these elements as needed. This is particularly valuable in the fast-moving world of technology, where today&#8217;s popular framework might be obsolete tomorrow. Similarly, you might start with a CLI for internal use, then add a web interface for broader access, all without altering your core business rules&#8217; code. Your core business logic remains stable, while you have the flexibility to adopt new technologies in the outer layers as they emerge. Lastly, Clean Architecture promotes long-term agility in development and leads to what Robert C. Martin calls a <em><a href="https://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html">Screaming Architecture</a></em>. Its focus on separating concerns and managing dependencies results in a codebase that&#8217;s easier to understand and modify. The concept of Screaming Architecture suggests that when you look at the structure of your system, it should scream its purpose and use cases, not its frameworks or tools. For instance, your architecture should scream <em>online bookstore</em>, not <em>Django application</em>. This clear, purpose-driven structure allows new team members to quickly grasp the system&#8217;s intent and make contributions. The architecture itself becomes a form of documentation, revealing the system&#8217;s core purpose and functionality at a glance. Such clarity and flexibility translate to increased development speed over the long term, even as the system grows in complexity. It also ensures that your system remains focused on its core business logic, rather than being tied to specific technical implementations.</p><h2>Clean Architecture in context</h2><p>To fully appreciate the value of Clean Architecture, it&#8217;s important to understand its place in the broader context of software development practices and methodologies.</p><p>Clean Architecture represents an evolution from traditional layered architecture. While it builds upon the concept of layers, it places a stronger emphasis on SoC and enforces the Dependency Rule more strictly than traditional architectures. Unlike traditional layered architectures where lower layers often depend on persistence or infrastructure concerns, Clean Architecture keeps the inner layers pure and focused on business logic. This shift in focus allows for greater flexibility and resilience to change.</p><p>Clean Architecture complements modern development practices such as Agile and DevOps. It aligns well with Agile methodologies by facilitating <strong>continuous delivery</strong> (<strong>CD</strong>) and making it easier to respond to change. The clear SoC supports iterative development and makes it easier to modify or extend functionality in response to changing requirements. In terms of DevOps, Clean Architecture supports practices such as <strong>continuous integration and deployment</strong> (<strong>CI/CD</strong>) by making systems more testable and modular. The clear boundaries between components can also help in scaling development across teams, as different teams can work on different layers or components with minimal interference.</p><p>In conclusion, Clean Architecture offers a powerful approach to building software systems that are scalable, maintainable, and adaptable to change. By focusing on SoC and managing dependencies, it provides a structure that can withstand the test of time and the pressures of evolving technology and business needs. As we move into the next section, we&#8217;ll explore how these principles align particularly well with Python development practices.</p><div><hr></div><h1>Clean Architecture and Python: a natural fit</h1><p>As we&#8217;ve explored the principles and benefits of Clean Architecture, you might be wondering how well these concepts align with Python development. In this section, we&#8217;ll discover that Clean Architecture and Python share a natural affinity, making Python an excellent language for implementing Clean Architecture principles.</p><p>Python&#8217;s philosophy, as embodied in <em><a href="https://peps.python.org/pep-0020/">The Zen of Python</a></em> aligns remarkably well with Clean Architecture principles. Both emphasize simplicity, readability, and the importance of well-structured code. Python&#8217;s focus on creating clear, maintainable, and adaptable code provides a strong foundation for implementing Clean Architecture. As we delve deeper into this section, we&#8217;ll explore how Python language features can be leveraged to create robust, maintainable systems that adhere to Clean Architecture principles.</p><h2>Implementing Clean Architecture in Python</h2><p>Python&#8217;s dynamic nature, combined with its strong support for <strong>object-oriented programming</strong> (<strong>OOP</strong>) and functional programming paradigms, allows developers to implement Clean Architecture concepts with less boilerplate and greater clarity than many other languages.</p><p><strong>Note on code examples</strong></p><p>Throughout this book, you&#8217;ll notice type annotations in our code examples (e.g., <code>def function(parameter: type) -&gt; return_type)</code>. These type hints enhance code clarity and help enforce Clean Architecture boundaries. We&#8217;ll explore this powerful feature in depth in <em><a href="https://subscription.packtpub.com/book/programming/9781836642893/3">Chapter 3</a></em>.</p><p>A key principle of Clean Architecture is the reliance on abstractions rather than concrete implementations. This principle directly supports the Dependency Rule we discussed earlier: dependencies should only point inward. Let&#8217;s see how this works in practice using Python&#8217;s <strong>abstract base classes</strong> (<strong>ABCs</strong>).</p><p>Consider the following example, which models a notification system:</p><pre><code><code>from abc import ABC, abstractmethod
class Notifier(ABC):
    @abstractmethod
    def send_notification(self, message: str) -&gt; None:
        pass
class EmailNotifier(Notifier):
    def send_notification(self, message: str) -&gt; None:
        print(f"Sending email: {message}")
class SMSNotifier(Notifier):
    def send_notification(self, message: str) -&gt; None:
        print(f"Sending SMS: {message}")
class NotificationService:
    def __init__(self, notifier: Notifier):
        self.notifier = notifier
    def notify(self, message: str) -&gt; None:
        self.notifier.send_notification(message)
# Usage
email_notifier = EmailNotifier()
email_service = NotificationService(email_notifier)
email_service.notify("Hello via email")
</code></code></pre><p>This example demonstrates key concepts of Clean Architecture using Python&#8217;s ABCs:</p><ol><li><p><strong>ABC</strong>: The <code>Notifier</code> class is an ABC, defining an interface that all notifier classes must follow. This represents an inner ring in our Clean Architecture structure.</p></li><li><p><strong>Abstract method</strong>: The <code>send_notification</code> method in <code>Notifier</code> is marked with <code>@abstractmethod</code>, enforcing implementation in subclasses.</p></li><li><p><strong>Concrete implementations</strong>: <code>EmailNotifier</code> and <code>SMSNotifier</code> are concrete classes in an outer ring. They inherit from <code>Notifier</code> and provide specific implementations.</p></li><li><p><strong>Dependency inversion</strong>: The <code>NotificationService</code> class depends on the abstract <code>Notifier</code> class, not on concrete implementations. This adheres to the Dependency Rule, as the abstract <code>Notifier</code> class (inner ring) doesn&#8217;t depend on the concrete notifiers (outer ring). We&#8217;ll dive deeper into dependency inversion in the next chapter.</p></li></ol><p>This structure embodies the Clean Architecture principles we&#8217;ve discussed:</p><ul><li><p><strong>It respects the Dependency Rule</strong>: The abstract <code>Notifier</code> class (inner ring) knows nothing about the concrete notifiers or the <code>NotificationService</code> class (outer rings)</p></li><li><p><strong>It allows for easy extension</strong>: We can add new types of notifiers (such as <code>PushNotifier</code>) without changing the <code>NotificationService</code> class</p></li><li><p><strong>It promotes flexibility and maintainability</strong>: The core business logic (sending a notification) is separated from the implementation details (how the notification is sent)</p></li></ul><p>By structuring our code this way, we create a system that&#8217;s not only flexible and maintainable but also adheres to the fundamental principles of Clean Architecture. The abstract <code>Notifier</code> class represents our core business rules, while the concrete notifiers and the <code>NotificationService</code> class represent the more volatile outer layers. This separation allows us to easily swap or add new notification methods without affecting the core logic of our application.</p><p>So, we&#8217;ve seen a simple ABC example, but this is where Python truly shines. We can implement the same Clean Architecture principles without using a class hierarchy, instead relying on Python&#8217;s support for <strong><a href="https://realpython.com/duck-typing-python/">duck typing</a></strong>. This flexibility is one of Python&#8217;s strengths, allowing developers to choose the approach that best fits their project&#8217;s needs while still adhering to Clean Architecture principles.</p><p>Duck typing is a programming concept where the suitability of an object is determined by the presence of certain methods or properties, rather than its explicit type. The name comes from the saying, &#8220;If it walks like a duck and quacks like a duck, then it must be a duck.&#8221; In duck typing, we don&#8217;t care about the object&#8217;s type; we care about whether it can do what we need it to do.</p><p>This approach aligns well with Clean Architecture&#8217;s emphasis on abstractions and interfaces. If you&#8217;d prefer to stay away from rigid class hierarchies, Python&#8217;s <code>Protocol</code> feature, introduced in <a href="https://peps.python.org/pep-0544/">Python 3.8</a>, offers the best of both worlds: duck typing with type hinting. Here&#8217;s an example that implements the same notification system using protocols:</p><pre><code><code>from typing import Protocol
class Notifier(Protocol):
    def send_notification(self, message: str) -&gt; None:
        ...
class EmailNotifier: # Note: no explicit inheritance
    def send_notification(self, message: str) -&gt; None:
        print(f"Sending email: {message}")
class SMSNotifier: # Note: no explicit inheritance
    def send_notification(self, message: str) -&gt; None:
        print(f"Sending SMS: {message}")
class NotificationService:
    # Still able to use type hinting
    def __init__(self, notifier: Notifier):
        self.notifier = notifier
    def notify(self, message: str) -&gt; None:
        self.notifier.send_notification(message)
# Usage
sms_notifier = SMSNotifier()
sms_service = NotificationService(sms_notifier)
sms_service.notify("Hello via SMS")
</code></code></pre><p>This example demonstrates the same notification system as before but using Python&#8217;s <code>Protocol</code> feature instead of ABCs. Let&#8217;s break down the key differences and their implications for Clean Architecture:</p><ul><li><p><strong>Protocol versus ABC</strong>: The <code>Notifier</code> class is now a <code>Protocol</code> class instead of an <code>ABC</code> class. It defines a structural subtyping interface rather than requiring explicit inheritance.</p></li><li><p><strong>Implicit conformance</strong>: The <code>EmailNotifier</code> and <code>SMSNotifier</code> classes don&#8217;t explicitly inherit from the <code>Notifier</code> class, but they conform to its interface by implementing the <code>send_notification</code> method.</p></li><li><p><strong>Duck typing with type hinting</strong>: This approach combines Python&#8217;s duck typing flexibility with the benefits of static type checking, aligning with Clean Architecture&#8217;s emphasis on loose coupling.</p></li><li><p><strong>Concrete implementations</strong>: The <code>NotificationService</code> class still depends on the abstract <code>Notifier</code> protocol, not concrete implementations, adhering to Clean Architecture principles.</p></li></ul><p>This protocol-based approach offers a flexible, Pythonic implementation of Clean Architecture concepts, balancing type safety with reduced class hierarchy rigidity. It demonstrates how to align Clean Architecture principles with Python&#8217;s philosophy, promoting adaptable and maintainable code.</p><p>We highly recommend the use of type hinting via either ABCs or protocols when implementing Clean Architecture. This approach, as opposed to simple implicit interfaces without type hinting, offers significant advantages:</p><ul><li><p>Improved code readability</p></li><li><p>Enhanced IDE support and earlier error detection</p></li><li><p>Better alignment with Clean Architecture goals</p></li></ul><p>In the remaining parts of the book, we&#8217;ll primarily use ABCs in our examples as they are in greater use in existing Python codebases. However, the principles discussed are equally applicable to protocol-based implementations, and readers can adapt the examples to use protocols if preferred.</p><h2>Practical example: a glimpse of Clean Architecture in a Python project</h2><p>To illustrate the concepts we&#8217;ve discussed, let&#8217;s examine the basic structure of a Clean Architecture Python project. This structure embodies the principles we&#8217;ve covered and demonstrates how they translate into a practical file organization. We&#8217;ll stay at a high level here; later chapters will cover real-world examples in full detail:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Kd79!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc3da56-3a64-403d-a1c2-c1f878660b96_718x1228.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Kd79!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc3da56-3a64-403d-a1c2-c1f878660b96_718x1228.png 424w, https://substackcdn.com/image/fetch/$s_!Kd79!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc3da56-3a64-403d-a1c2-c1f878660b96_718x1228.png 848w, https://substackcdn.com/image/fetch/$s_!Kd79!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc3da56-3a64-403d-a1c2-c1f878660b96_718x1228.png 1272w, https://substackcdn.com/image/fetch/$s_!Kd79!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc3da56-3a64-403d-a1c2-c1f878660b96_718x1228.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Kd79!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc3da56-3a64-403d-a1c2-c1f878660b96_718x1228.png" width="718" height="1228" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0fc3da56-3a64-403d-a1c2-c1f878660b96_718x1228.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1228,&quot;width&quot;:718,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 1.2: A potential layout for a Clean Architecture Python web application&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 1.2: A potential layout for a Clean Architecture Python web application" title="Figure 1.2: A potential layout for a Clean Architecture Python web application" srcset="https://substackcdn.com/image/fetch/$s_!Kd79!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc3da56-3a64-403d-a1c2-c1f878660b96_718x1228.png 424w, https://substackcdn.com/image/fetch/$s_!Kd79!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc3da56-3a64-403d-a1c2-c1f878660b96_718x1228.png 848w, https://substackcdn.com/image/fetch/$s_!Kd79!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc3da56-3a64-403d-a1c2-c1f878660b96_718x1228.png 1272w, https://substackcdn.com/image/fetch/$s_!Kd79!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc3da56-3a64-403d-a1c2-c1f878660b96_718x1228.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 1.2: A potential layout for a Clean Architecture Python web application</em></p><p>This file structure in <em>Figure 1.2</em> exemplifies the Clean Architecture principles we&#8217;ve discussed:</p><ol><li><p><strong>SoC</strong>: Each directory represents a distinct layer of the application, aligning with the concentric circles we saw in <em>Figure 1.1</em>.</p></li><li><p><strong>Dependency Rule</strong>: The structure enforces the Dependency Rule we discussed earlier. If we were to investigate the inner layers (<code>entities</code> and <code>use_cases</code>), we would not see any imports from the outer layers.</p></li><li><p><strong>Entities layer</strong>: The <code>entities</code> directory contains the core business objects, such as <code>user.py</code>. These are at the center of our Clean Architecture diagram and have no dependencies on outer layers.</p></li><li><p><strong>Use Cases layer</strong>: The <code>use_cases</code> directory holds the application-specific business rules. It depends on the entities but is independent of the outer layers.</p></li><li><p><strong>Interface Adapters layer</strong>: The <code>interfaces</code> directory contains controllers, presenters, and gateways. These adapt data between use cases and external agencies (such as web frameworks or databases).</p></li><li><p><strong>Frameworks layer</strong>: The outermost <code>frameworks</code> directory contains implementations of external interfaces, such as database <strong>object-relational mappers</strong> (<strong>ORMs</strong>) or web frameworks.</p></li><li><p><strong>Straightforward testing</strong>: The <code>tests</code> directory structure mirrors the application structure, allowing for comprehensive testing at all levels.</p></li></ol><p>This structure supports the key benefits of Clean Architecture we discussed:</p><ul><li><p><strong>Maintainability</strong>: Changes to external components (in the <code>frameworks</code> directory) don&#8217;t affect the core business logic in entities and use cases</p></li><li><p><strong>Flexibility</strong>: We can easily swap out the database or web framework in the <code>frameworks</code> directory without touching the business logic</p></li><li><p><strong>Testability</strong>: The clear separation allows for easy unit testing of core components and integration testing of interfaces</p></li></ul><p>Remember our discussion about abstractions? The <code>interfaces</code> directory is where we&#8217;d implement the ABCs or protocols we talked about. For instance, <code>user_repository.py</code> might define an abstract <code>UserRepository</code> class, which is then implemented concretely in the <code>frameworks/database/orm.py</code> file.</p><p>This structure also facilitates the <em>master plan</em> we mentioned earlier. It provides a clear roadmap for where new code should be placed, helping developers make consistent decisions even as the project grows and evolves.</p><p>By organizing our Python project this way, we&#8217;re setting ourselves up for long-term success, creating a codebase that&#8217;s not just functional but also maintainable, flexible, and aligned with Clean Architecture principles.</p><h2>Python-specific considerations and potential pitfalls</h2><p>While Clean Architecture and Python are highly compatible, there are some important considerations to be aware of when implementing these principles in Python projects. Throughout this book, we&#8217;ll guide you through mitigating these concerns, providing practical solutions and best practices.</p><h3>Balancing Pythonic code with architectural principles</h3><p>Python&#8217;s <em>batteries included</em> philosophy and an extensive standard library can sometimes tempt developers to bypass architectural boundaries for the sake of convenience. However, maintaining a clean architecture often involves creating abstractions around even standard library functions to maintain SoC. For example, instead of directly using Python&#8217;s <code>smtplib</code> library in your use cases, consider creating an abstraction layer for sending notifications.</p><p>As we progress through this book, we&#8217;ll demonstrate how this effort of creating abstractions pays off in terms of maintainability, flexibility, and testability. You&#8217;ll see that the initial investment in Clean Architecture principles yields significant long-term benefits.</p><p>Python&#8217;s ease of importing can sometimes lead to messy dependency structures, as all packages are effectively public. We&#8217;ll show you how to be vigilant about maintaining the Dependency Rule, ensuring that inner layers don&#8217;t depend on outer layers. In <em><a href="https://subscription.packtpub.com/book/programming/9781836642893/2">Chapter 2</a></em>, we&#8217;ll explore techniques and tools to help you maintain clean dependency structures in your Python projects.</p><h3>Scaling Clean Architecture in Python projects</h3><p>The application of Clean Architecture principles should be tailored to the size and complexity of your Python project.</p><p>For instance, in small projects or quick prototypes, it&#8217;s perfectly fine to have a simple, monolithic architecture. However, even in these cases, building in a thoughtful, modular manner can set the stage for future growth. You might start with a simple structure:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AkGu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f89c92-294d-4045-bff0-554020b53ec0_315x249.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AkGu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f89c92-294d-4045-bff0-554020b53ec0_315x249.png 424w, https://substackcdn.com/image/fetch/$s_!AkGu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f89c92-294d-4045-bff0-554020b53ec0_315x249.png 848w, https://substackcdn.com/image/fetch/$s_!AkGu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f89c92-294d-4045-bff0-554020b53ec0_315x249.png 1272w, https://substackcdn.com/image/fetch/$s_!AkGu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f89c92-294d-4045-bff0-554020b53ec0_315x249.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AkGu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f89c92-294d-4045-bff0-554020b53ec0_315x249.png" width="315" height="249" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d5f89c92-294d-4045-bff0-554020b53ec0_315x249.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:249,&quot;width&quot;:315,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 1.3: Quick prototype Python project&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 1.3: Quick prototype Python project" title="Figure 1.3: Quick prototype Python project" srcset="https://substackcdn.com/image/fetch/$s_!AkGu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f89c92-294d-4045-bff0-554020b53ec0_315x249.png 424w, https://substackcdn.com/image/fetch/$s_!AkGu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f89c92-294d-4045-bff0-554020b53ec0_315x249.png 848w, https://substackcdn.com/image/fetch/$s_!AkGu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f89c92-294d-4045-bff0-554020b53ec0_315x249.png 1272w, https://substackcdn.com/image/fetch/$s_!AkGu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f89c92-294d-4045-bff0-554020b53ec0_315x249.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 1.3: Quick prototype Python project</em></p><p>In this small project, you can still apply Clean Architecture principles by doing the following:</p><ul><li><p>Keeping business logic in <code>models.py</code> separate from presentation logic in <code>views.py</code></p></li><li><p>Using <strong>dependency injection</strong> (<strong>DI</strong>) to make components more modular and testable</p></li><li><p>Defining clear interfaces between modules</p></li></ul><p>As your project grows, you can gradually evolve toward a more comprehensive Clean Architecture structure. This evolution might involve the following:</p><ol><li><p>Separating core business logic (entities and use cases) into their own modules</p></li><li><p>Introducing interfaces to abstract away framework-specific code</p></li><li><p>Organizing tests to align with the architectural layers</p></li></ol><p>This book takes a hands-on approach, starting with a basic application and a pragmatic application of Clean Architecture principles. As we progress, the complexity of our example application will increase, demonstrating how to evolve the Clean Architecture approach as the codebase grows.</p><p>Clean Architecture is a spectrum, not a binary choice. The patterns we&#8217;ll explore represent a comprehensive implementation designed to showcase Clean Architecture&#8217;s full capabilities, but in practice, you might choose to implement only the patterns that provide clear value for your specific context. A small API might benefit from clean controller patterns without needing full presenter abstractions, while a data processing script might adopt domain entities while skipping interface adapters entirely. You&#8217;ll learn how to apply these principles judiciously, avoiding over-engineering in smaller projects while leveraging the full power of Clean Architecture in larger systems. The key is understanding what each pattern provides so you can make informed decisions about which architectural boundaries matter most for your project.</p><h3>Leveraging Python&#8217;s dynamic nature appropriately</h3><p>While Python&#8217;s dynamic nature is powerful, it can also lead to issues if not used carefully. <em><a href="https://subscription.packtpub.com/book/programming/9781836642893/3">Chapter 3</a></em> is devoted to aspects of Python&#8217;s dynamic nature, including duck typing, the use of type hints, and newer features such as protocols. By the end of this chapter, you&#8217;ll have a solid foundation on how to best leverage these language features to support a Clean Architecture approach, balancing Python&#8217;s flexibility with architectural rigor.</p><h3>Testing considerations</h3><p>This book, as with Clean Architecture itself, strongly promotes the use of tests. Tests are essentially first-class clients of your application code, using the codebase and making assertions on the results. The same architectural considerations that apply to your main codebase also apply to your Python tests.</p><p>We&#8217;ll guide you through writing tests that respect architectural boundaries. You&#8217;ll learn to recognize when your tests are indicating potential issues in your architecture, such as when they require excessive setup or mocking. In the test cases for each chapter&#8217;s code examples and culminating in <em><a href="https://subscription.packtpub.com/book/programming/9781836642893/8">Chapter 8</a></em>, we&#8217;ll explore these concepts in depth, showing you how to use tests not just for verification, but as a tool for maintaining and improving your architecture.</p><p>By being aware of these considerations and potential pitfalls and following the guidance provided throughout this book, you can create Python systems that are both clean and practical, leveraging the strengths of both Clean Architecture and Python. Remember, the key is to apply these principles thoughtfully, always with an eye toward creating maintainable, testable, and flexible Python code.</p><div><hr></div><h1>Summary</h1><p>In this chapter, we introduced Clean Architecture at a high level and its relevance to Python development. We gave you context by exploring the evolution of software architecture, from Waterfall to Agile, highlighting persistent challenges in managing complexity, accommodating change, and maintaining long-term productivity.</p><p>We introduced Clean Architecture&#8217;s core principles:</p><ul><li><p>Separation of concerns (SoC)</p></li><li><p>Independence of external details</p></li><li><p>Testability and maintainability</p></li></ul><p>We examined Clean Architecture&#8217;s general structure, from core entities and use cases to outer layers of interface adapters, frameworks, and drivers, emphasizing how this structure promotes maintainability and flexibility. We discussed the benefits of Clean Architecture, including improved adaptability, enhanced testability, and long-term development agility, and how it complements modern development practices such as Agile and DevOps.</p><p>Furthermore, we explored the natural fit between Clean Architecture and Python, addressing how Python&#8217;s features can be leveraged to implement Clean Architecture effectively. We also highlighted Python-specific considerations and potential pitfalls, emphasizing the need to balance Pythonic code with architectural principles and adapt Clean Architecture to different project sizes.</p><p>In this chapter, we introduced the primary goals of Clean Architecture and explored its natural fit with Python development. We saw how Clean Architecture principles can be implemented using Python&#8217;s features such as ABCs and protocols, providing a foundation for creating maintainable and flexible software systems.</p><p>In the next chapter, we&#8217;ll build upon this foundation by diving into the SOLID principles. These principles, which form the bedrock of Clean Architecture, will be explored in depth with practical Python examples, showing how they contribute to robust and extensible application design.</p><div><hr></div><h1>Further reading</h1><p>To learn more about the topics that were covered in this chapter, take a look at the following resources:</p><ul><li><p><em>Clean Architecture: A Craftsman&#8217;s Guide to Software Structure and Design</em> by Robert C. Martin. This book provides a comprehensive look at Clean Architecture from its originator.</p></li><li><p><em>Domain-Driven Design: Tackling Complexity in the Heart of Software</em> by Eric Evans. While not specific to Clean Architecture, this book provides valuable insights into designing software around business domains.</p></li><li><p><em>Agile Software Development, Principles, Patterns, and Practices</em> by Robert C. Martin. This book covers many of the principles that underpin Clean Architecture in the context of Agile development.</p></li><li><p><em>The Pragmatic Programmer: Your Journey to Mastery</em> by Andrew Hunt and David Thomas. This classic book offers practical advice on software design and development that aligns well with Clean Architecture principles.</p></li></ul><div><hr></div><p>To learn Clean Architecture through a series of real-world, code-centric examples and exercises, optimize system componentization, and significantly reduce maintenance burden and overall complexity, check out <em><strong><a href="https://www.packtpub.com/en-us/product/clean-architecture-with-python-9781836642886">Clean Architecture with Python</a></strong></em> by <span class="mention-wrap" data-attrs="{&quot;name&quot;:&quot;Sam Keen&quot;,&quot;id&quot;:11641009,&quot;type&quot;:&quot;user&quot;,&quot;url&quot;:null,&quot;photo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/0463b396-9180-4110-872c-fa7218339e5b_98x98.jpeg&quot;,&quot;uuid&quot;:&quot;38f18b45-5075-4dd4-86d5-b2179584f1db&quot;}" data-component-name="MentionToDOM"></span>. The book helps you apply Clean Architecture concepts confidently to new Python projects and legacy code refactoring.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.packtpub.com/en-us/product/clean-architecture-with-python-9781836642886" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NtED!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7059d45c-81bb-4525-8a53-28d479a5593b_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!NtED!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7059d45c-81bb-4525-8a53-28d479a5593b_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!NtED!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7059d45c-81bb-4525-8a53-28d479a5593b_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!NtED!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7059d45c-81bb-4525-8a53-28d479a5593b_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NtED!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7059d45c-81bb-4525-8a53-28d479a5593b_2250x2775" width="268" height="330.5824175824176" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7059d45c-81bb-4525-8a53-28d479a5593b_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:268,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Clean Architecture with Python&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.packtpub.com/en-us/product/clean-architecture-with-python-9781836642886&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Clean Architecture with Python" title="Clean Architecture with Python" srcset="https://substackcdn.com/image/fetch/$s_!NtED!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7059d45c-81bb-4525-8a53-28d479a5593b_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!NtED!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7059d45c-81bb-4525-8a53-28d479a5593b_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!NtED!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7059d45c-81bb-4525-8a53-28d479a5593b_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!NtED!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7059d45c-81bb-4525-8a53-28d479a5593b_2250x2775 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p>]]></content:encoded></item><item><title><![CDATA[When to Use Object-Oriented Programming]]></title><description><![CDATA[The complete &#8220;Chapter 5: When to Use Object-Oriented Programming&#8221; from the book Python Object-Oriented Programming, Fourth Edition by Steven F. Lott and Dusty Phillips (Packt, July 2021).]]></description><link>https://deepengineering.substack.com/p/when-to-use-object-oriented-programming</link><guid isPermaLink="false">https://deepengineering.substack.com/p/when-to-use-object-oriented-programming</guid><dc:creator><![CDATA[Steven Lott]]></dc:creator><pubDate>Wed, 27 Aug 2025 08:36:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!S24_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0059565-ab59-4421-90dd-de189e63939f_810x1000" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In previous chapters, we've covered many of the defining features of object-oriented programming. We now know some principles and paradigms of object-oriented design, and we've covered the syntax of object-oriented programming in Python.</p><p>Yet, we don't know exactly how and, especially, when to utilize these principles and syntax in practice. In this chapter, we'll discuss some useful applications of the knowledge we've gained, looking at some new topics along the way:</p><ul><li><p>How to recognize objects</p></li><li><p>Data and behaviors, once again</p></li><li><p>Wrapping data behaviors using properties</p></li><li><p>The Don't Repeat Yourself principle and avoiding repetition</p></li></ul><p>In this chapter, we'll also address some alternative designs for our case study problem. We'll look at ways to partition the sample data into training sets and test sets.</p><p>We'll start this chapter with a close look at the nature of objects and their internal state. There are cases when there's no state change, and a class definition isn't desirable.</p><div><hr></div><h1>Treat objects as objects</h1><p>This may seem obvious; you should generally give separate objects in your problem domain a special class in your code. We've seen examples of this in the case studies in previous chapters: first, we identify objects in the problem, and then model their data and behaviors.</p><p>Identifying objects is a very important task in object-oriented analysis and programming. But it isn't always as easy as counting the nouns in short paragraphs that, frankly, the authors have constructed explicitly for that purpose. Remember, objects are things that have both data and behavior. If we are working only with data, we are often better off storing it in a list, set, dictionary, or other Python data structure (which we'll be covering thoroughly in <em>Chapter 7</em>, <em>Python Data Structures</em>). On the other hand, if we are working only with behavior, but no stored data, a simple function is more suitable.</p><p>An object, however, has both data and behavior. Proficient Python programmers use built-in data structures unless (or until) there is an obvious need to define a class. There is no reason to add an extra level of complexity if it doesn't help organize our code. On the other hand, the need is not always self-evident.</p><p>We can often start our Python programs by storing data in a few variables. As the program expands, we will later find that we are passing the same set of related variables to a set of functions. This is the time to think about grouping both variables and functions into a class.</p><p>For example, if we are designing a program to model polygons in two-dimensional space, we might start with each polygon represented as a list of points. The points would be modeled as two tuples (x, y) describing where that point is located. This is all data, stored in a set of nested data structures (specifically, a list of tuples). We can (and often do) start hacking at the command prompt:</p><pre><code><code>&gt;&gt;&gt; square = [(1,1), (1,2), (2,2), (2,1)] </code></code></pre><p>Now, if we want to calculate the distance around the perimeter of the polygon, we need to sum the distances between each point. To do this, we need a function to calculate the distance between two points. Here are two such functions:</p><pre><code><code>&gt;&gt;&gt; from math import hypot
&gt;&gt;&gt; def distance(p_1, p_2):
...     return hypot(p_1[0]-p_2[0], p_1[1]-p_2[1])
&gt;&gt;&gt; def perimeter(polygon):
...     pairs = zip(polygon, polygon[1:]+polygon[:1])
...     return sum(
...         distance(p1, p2) for p1, p2 in pairs
...     )</code></code></pre><p>We can exercise the functions to check our work:</p><pre><code><code>&gt;&gt;&gt; perimeter(square)
4.0</code></code></pre><p>This is a start, but it's not completely descriptive of the problem domain. We can kind of see what a polygon might be. But we need to read the entire batch of code to see how the two functions work together.</p><p>We can add type hints to help clarify the intent behind each function. The result looks like this:</p><pre><code><code>from __future__ import annotations
from math import hypot
from typing import Tuple, List
Point = Tuple[float, float]
def distance(p_1: Point, p_2: Point) -&gt; float:
    return hypot(p_1[0] - p_2[0], p_1[1] - p_2[1])
Polygon = List[Point]
def perimeter(polygon: Polygon) -&gt; float:
    pairs = zip(polygon, polygon[1:] + polygon[:1])
    return sum(distance(p1, p2) for p1, p2 in pairs)</code></code></pre><p>We've added two type definitions, <code>Point</code> and <code>Polygon</code>, to help clarify our intentions. The definition of <code>Point</code> shows how we'll use the built-in <code>tuple</code> class. The definition of <code>Polygon</code> shows how the built-in <code>list</code> class builds on the <code>Point</code> class.</p><p>When writing annotations inside method parameter definitions, we can generally use the type name directly, for example, <code>def method(self, values: list[int]) -&gt; None:</code>. For this to work, we need to use <code>from __future__ import annotations</code>. When defining a new type hint, however, we need to use the names from the <code>typing</code> module. That's why the definition of the new <code>Point</code> type uses <code>typing.Tuple</code> in the expression <code>Tuple[float, float]</code>.</p><p>Now, as object-oriented programmers, we clearly recognize that a <code>polygon</code> class could encapsulate the list of points (data) and the <code>perimeter</code> function (behavior). Further, a <code>Point</code> class, such as we defined in <em>Chapter 2</em>, <em>Objects in Python</em>, might encapsulate the <code>x</code> and <code>y</code> coordinates and the <code>distance</code> method. The question is: is it valuable to do this?</p><p>For the previous code, maybe yes, maybe no. With our recent experience in object-oriented principles, we can write an object-oriented version in record time. Let's compare them as follows:</p><pre><code><code>from math import hypot
from typing import Tuple, List, Optional, Iterable
class Point:
    def __init__(self, x: float, y: float) -&gt; None:
        self.x = x
        self.y = y
    def distance(self, other: "Point") -&gt; float:
        return hypot(self.x - other.x, self.y - other.y)
class Polygon:
    def __init__(self) -&gt; None:
        self.vertices: List[Point] = []
    def add_point(self, point: Point) -&gt; None:
        self.vertices.append((point))
    def perimeter(self) -&gt; float:
        pairs = zip(
            self.vertices, self.vertices[1:] + self.vertices[:1])
        return sum(p1.distance(p2) for p1, p2 in pairs)</code></code></pre><p>There seems to be almost twice as much code here as there was in our earlier version, although we could argue that the <code>add_point</code> method is not strictly necessary. We could also try to insist on using <code>_vertices</code> to discourage the use of the attribute, but the use of leading <code>_</code> variable names doesn't seem to really solve the problem.</p><p>Now, to understand the differences between the two classes a little better, let's compare the two APIs in use. Here's how to calculate the perimeter of a square using the object-oriented code:</p><pre><code><code>&gt;&gt;&gt; square = Polygon()
&gt;&gt;&gt; square.add_point(Point(1,1))
&gt;&gt;&gt; square.add_point(Point(1,2))
&gt;&gt;&gt; square.add_point(Point(2,2))
&gt;&gt;&gt; square.add_point(Point(2,1))
&gt;&gt;&gt; square.perimeter()
4.0</code></code></pre><p>That's fairly succinct and easy to read, you might think, but let's compare it to the function-based code:</p><pre><code><code>&gt;&gt;&gt; square = [(1,1), (1,2), (2,2), (2,1)]
&gt;&gt;&gt; perimeter(square)
4.0</code></code></pre><p>Hmm, maybe the object-oriented API isn't so compact! Our first, hacked-in version, without type hints or class definitions, is the shortest. How do we know what the list of tuples is supposed to represent? How do we remember what kind of object we're supposed to pass into the <code>perimeter</code> function? We needed some documentation to explain how the first set of functions should be used.</p><p>The functions annotated with type hints were quite a bit easier to understand, as were the class definitions. The relationships among the objects are more clearly defined by hints or classes or both.</p><p>Code length is not a good indicator of code complexity. Some programmers get hung up on complicated <em>one-liners</em> that do an incredible amount of work in one line of code. This can be a fun exercise, but the result is often unreadable, even to the original author the following day. Minimizing the amount of code can often make a program easier to read, but do not blindly assume this is the case.</p><p>No one wins at code golf. Minimizing the volume of code is rarely desirable.</p><p>Luckily, this trade-off isn't necessary. We can make the object-oriented <code>Polygon</code> API as easy to use as the functional implementation. All we have to do is alter our <code>Polygon</code> class so that it can be constructed with multiple points.</p><p>Let's give it an initializer that accepts a list of <code>Point</code> objects:</p><pre><code><code>class Polygon_2:
    def __init__(self, vertices: Optional[Iterable[Point]] = None) -&gt; None:
        self.vertices = list(vertices) if vertices else []
    def perimeter(self) -&gt; float:
        pairs = zip(
            self.vertices, self.vertices[1:] + self.vertices[:1])
        return sum(p1.distance(p2) for p1, p2 in pairs)</code></code></pre><p>For the <code>perimeter()</code> method, we've used the <code>zip()</code> function to create pairs of vertices, with items drawn from two lists to create a sequence of pairs. One list provided to <code>zip()</code> is the complete sequence of vertices. The other list of vertices starts from vertex 1 (not 0) and ends with the vertex before 1 (that is, vertex 0). For a triangle, this will make three pairs: <code>(v[0], v[1])</code>, <code>(v[1], v[2])</code>, and <code>(v[2], v[0])</code>. We can then compute the distance between the pairs using <code>Point.distance()</code>. Finally, we sum the sequence of distances. This seems to improve things considerably. We can now use this class like the original hacked-in function definitions:</p><pre><code><code>&gt;&gt;&gt; square = Polygon_2(
... [Point(1,1), Point(1,2), Point(2,2), Point(2,1)]
... )
&gt;&gt;&gt; square.perimeter()
4.0
</code></code></pre><p>It's handy to have the details of the individual method definitions. We've built an API that's close to the original, succinct set of definitions. We've added enough formality to be confident the code is likely to work before we even start putting test cases together.</p><p>Let's take one more step. Let's allow it to accept tuples too, and we can construct the <code>Point</code> objects ourselves, if needed:</p><pre><code><code>Pair = Tuple[float, float]
Point_or_Tuple = Union[Point, Pair]
class Polygon_3:
    def __init__(self, vertices: Optional[Iterable[Point_or_Tuple]] = None) -&gt; None:
        self.vertices: List[Point] = []
        if vertices:
            for point_or_tuple in vertices:
                self.vertices.append(self.make_point(point_or_tuple))
    @staticmethod
    def make_point(item: Point_or_Tuple) -&gt; Point:
        return item if isinstance(item, Point) else Point(*item)</code></code></pre><p>This initializer goes through the list of items (either <code>Point</code> or <code>Tuple[float, float]</code>) and ensures that any non-<code>Point</code> objects are converted to <code>Point</code> instances.</p><p>If you are experimenting with the above code, you should also define these variant class designs by creating subclasses of <code>Polygon</code> and overriding the <code>__init__()</code> method. Extending a class with dramatically different method signatures can raise error flags from <em><strong>mypy</strong></em>.</p><p>For an example this small, there's no clear winner between the object-oriented and more data-oriented versions of this code. They all do the same thing. If we have new functions that accept a polygon argument, such as <code>area(polygon)</code> or <code>point_in_polygon(polygon, x, y)</code>, the benefits of the object-oriented code become increasingly obvious. Likewise, if we add other attributes to the polygon, such as <code>color</code> or <code>texture</code>, it makes more and more sense to encapsulate that data into a single class.</p><p>The distinction is a design decision, but in general, the more important a set of data is, the more likely it is to have multiple functions specific to that data, and the more useful it is to use a class with attributes and methods instead.</p><p>When making this decision, it also pays to consider how the class will be used. If we're only trying to calculate the perimeter of one polygon in the context of a much greater problem, using a function will probably be quickest to code and easier to use <em>one time only</em>. On the other hand, if our program needs to manipulate numerous polygons in a wide variety of ways (calculating the perimeter, area, and intersection with other polygons, moving or scaling them, and so on), we have almost certainly identified a class of related objects. The class definition becomes more important as the number of instances increases.</p><p>Additionally, pay attention to the interaction between objects. Look for inheritance relationships; inheritance is impossible to model elegantly without classes, so make sure to use them. Look for the other types of relationships we discussed in <em>Chapter 1</em>, <em>Object-Oriented Design</em>, association and composition.</p><p>Composition can, technically, be modeled using only data structures &#8211; for example, we can have a list of dictionaries holding tuple values &#8211; but it is sometimes less complicated to create a few classes of objects, especially if there is behavior associated with the data.</p><p>One size does not fit all. The built-in, generic collections and functions work well for a large number of simple cases. A class definition works well for a large number of more complex cases. The boundary is hazy at best.</p><div><hr></div><h1>Adding behaviors to class data with properties</h1><p>Throughout this book, we've focused on the separation of behavior and data. This is very important in object-oriented programming, but we're about to see that, in Python, the distinction is uncannily blurry. Python is very good at blurring distinctions; it doesn't exactly help us to <em>think outside the box</em>. Rather, it teaches us to stop thinking about the box.</p><p>Before we get into the details, let's discuss some bad object-oriented design principles. Many object-oriented developers teach us to never access attributes directly. They insist that we write attribute access like this:</p><pre><code><code>class Color:
    def __init__(self, rgb_value: int, name: str) -&gt; None:
        self._rgb_value = rgb_value
        self._name = name
    def set_name(self, name: str) -&gt; None:
        self._name = name
    def get_name(self) -&gt; str:
        return self._name
    def set_rgb_value(self, rgb_value: int) -&gt; None:
        self._rgb_value = rgb_value
    def get_rgb_value(self) -&gt; int:
        return self._rgb_value</code></code></pre><p>The instance variables are prefixed with an underscore to suggest that they are private (other languages would actually force them to be private). Then, the <code>get</code> and <code>set</code> methods provide access to each variable. This class would be used in practice as follows:</p><pre><code><code>&gt;&gt;&gt; c = Color(0xff0000, "bright red")
&gt;&gt;&gt; c.get_name()
'bright red'
&gt;&gt;&gt; c.set_name("red")
&gt;&gt;&gt; c.get_name()
'red'</code></code></pre><p>The above example is not nearly as readable as the direct access version that Python favors:</p><pre><code><code>class Color_Py:
    def __init__(self, rgb_value: int, name: str) -&gt; None:
        self.rgb_value = rgb_value
        self.name = name</code></code></pre><p>Here's how this class works. It's slightly simpler:</p><pre><code><code>&gt;&gt;&gt; c = Color_Py(0xff0000, "bright red")
&gt;&gt;&gt; c.name
'bright red'
&gt;&gt;&gt; c.name = "red"
&gt;&gt;&gt; c.name
'red'</code></code></pre><p>So, why would anyone insist upon the method-based syntax?</p><p>The idea of setters and getters seems helpful for encapsulating the class definitions. Some Java-based tools can generate all the getters and setters automagically, making them almost invisible. Automating their creation doesn't make them a great idea. The most important historical reason for having getters and setters was to make the separate compilation of binaries work out in a tidy way. Without a need to link separately compiled binaries, this technique doesn't always apply to Python.</p><p>One ongoing justification for getters and setters is that, someday, we may want to add extra code when a value is set or retrieved. For example, we could decide to cache a value to avoid complex computations, or we might want to validate that a given value is a suitable input.</p><p>For example, we could decide to change the <code>set_name()</code> method as follows:</p><pre><code><code>class Color_V:
    def __init__(self, rgb_value: int, name: str) -&gt; None:
        self._rgb_value = rgb_value
        if not name:
            raise ValueError(f"Invalid name {name!r}")
        self._name = name
    def set_name(self, name: str) -&gt; None:
        if not name:
            raise ValueError(f"Invalid name {name!r}")
        self._name = name</code></code></pre><p>If we had written our original code for direct attribute access, and then later changed it to a method like the preceding one, we'd have a problem: anyone who had written code that accessed the attribute directly would now have to change their code to access a method. If they didn't then change the access style from attribute access to a function call, their code would be broken.</p><p>The mantra that we should make all attributes private, accessible through methods, doesn't make much sense in Python. The Python language lacks any real concept of private members! We can see the source; we often say "We're all adults here." What can we do? We can make the syntax distinction between attribute and method less visible.</p><p>Python gives us the <code>property</code> function to make methods that <em>look</em> like attributes. We can therefore write our code to use direct member access, and if we ever unexpectedly need to alter the implementation to do some calculation when getting or setting that attribute's value, we can do so without changing the interface. Let's see how it looks:</p><pre><code><code>class Color_VP:
    def __init__(self, rgb_value: int, name: str) -&gt; None:
        self._rgb_value = rgb_value
        if not name:
            raise ValueError(f"Invalid name {name!r}")
        self._name = name
    def _set_name(self, name: str) -&gt; None:
        if not name:
            raise ValueError(f"Invalid name {name!r}")
        self._name = name
    def _get_name(self) -&gt; str:
        return self._name
    name = property(_get_name, _set_name)</code></code></pre><p>Compared to the earlier class, we first change the <code>name</code> attribute into a (semi-)private <code>_name</code> attribute. Then, we add two more (semi-)private methods to get and set that variable, performing our validation when we set it.</p><p>Finally, we have the <code>property</code> construction at the bottom. This is the Python magic. It creates a new attribute on the <code>Color</code> class called <code>name</code>. It sets this attribute to be a <strong>property</strong>. Under the hood, a <code>property</code> attribute delegates the real work to the two methods we just created. When used in an access context (on the right side of the <code>=</code> or <code>:=</code>), the first function gets the value. When used in an update context (on the left side of <code>=</code> or <code>:=</code>), the second function sets the value.</p><p>This new version of the <code>Color</code> class can be used in exactly the same way as the earlier version, yet it now performs validation when we set the <code>name</code> attribute:</p><pre><code><code>&gt;&gt;&gt; c = Color_VP(0xff0000, "bright red")
&gt;&gt;&gt; c.name
'bright red'
&gt;&gt;&gt; c.name = "red"
&gt;&gt;&gt; c.name
'red'
&gt;&gt;&gt; c.name = ""
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
  File "setting_name_property.py", line 8, in _set_name
    raise ValueError(f"Invalid name {name!r}")
ValueError: Invalid name ''</code></code></pre><p>So, if we'd previously written code to access the <code>name</code> attribute, and then changed it to use our <code>property</code>-based object, the previous code would still work. If it attempts to set an empty <code>property</code> value, this is behavior we wanted to forbid. Success!</p><p>Bear in mind that, even with the <code>name</code> property, the previous code is not 100% safe. People can still access the <code>_name</code> attribute directly and set it to an empty string if they want to. But if they access a variable we've explicitly marked with an underscore to suggest it is private, they're the ones that have to deal with the consequences, not us. We established a formal contract, and if they elect to break the contract, they own the consequences.</p><h2>Properties in detail</h2><p>Think of the <code>property</code> function as returning an object that proxies any requests to get or set the attribute value through the method names we have specified. The <code>property</code> built-in is like a constructor for such an object, and that object is set as the public-facing member for the given attribute.</p><p>This <code>property</code> constructor can actually accept two additional arguments, a <code>delete</code> function and a docstring for the property. The <code>delete</code> function is rarely supplied in practice, but it can be useful for logging the fact that a value has been deleted, or possibly to veto deleting if we have reason to do so. The docstring is just a string describing what the property does, no different from the docstrings we discussed in <em>Chapter 2</em>, <em>Objects in Python</em>. If we do not supply this parameter, the docstring will instead be copied from the docstring for the first argument: the <code>getter</code> method.</p><p>Here is a silly example that states whenever any of the methods are called:</p><pre><code><code>class NorwegianBlue:
    def __init__(self, name: str) -&gt; None:
        self._name = name
        self._state: str
    def _get_state(self) -&gt; str:
        print(f"Getting {self._name}'s State")
        return self._state
    def _set_state(self, state: str) -&gt; None:
        print(f"Setting {self._name}'s State to {state!r}")
        self._state = state
    def _del_state(self) -&gt; None:
        print(f"{self._name} is pushing up daisies!")
        del self._state
    silly = property(
        _get_state, _set_state, _del_state, 
        "This is a silly property")</code></code></pre><p>Note that the <code>state</code> attribute has a type hint, <code>str</code>, but no initial value. It can be deleted, and only exists for part of the life of a <code>NorwegianBlue</code>. We need to provide a hint to help <em><strong>mypy</strong></em> understand what the type should be. But we don't assign a default value because that's the job of the <code>setter</code> method.</p><p>If we actually use an instance of this class, it does indeed print out the correct strings when we ask it to:</p><pre><code><code>&gt;&gt;&gt; p = NorwegianBlue("Polly")
&gt;&gt;&gt; p.silly = "Pining for the fjords"
Setting Polly's State to 'Pining for the fjords'
&gt;&gt;&gt; p.silly
Getting Polly's State
'Pining for the fjords'
&gt;&gt;&gt; del p.silly
Polly is pushing up daisies!</code></code></pre><p>Further, if we look at the help text for the <code>Silly</code> class (by issuing <code>help(Silly)</code> at the interpreter prompt), it shows us the custom docstring for our <code>silly</code> attribute:</p><pre><code><code>Help on class NorwegianBlue in module colors:
class NorwegianBlue(builtins.object)
 |  NorwegianBlue(name: str) -&gt; None
 |  
 |  Methods defined here:
 |  
 |  __init__(self, name: str) -&gt; None
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  silly
 |      This is a silly property</code></code></pre><p>Once again, everything is working as we planned. In practice, properties are normally only defined with the first two parameters: the <code>getter</code> and <code>setter</code> functions. If we want to supply a docstring for a property, we can define it on the <code>getter</code> function; the property proxy will copy it into its own docstring. The <code>delete</code> function is often left empty because object attributes are so rarely deleted.</p><h2>Decorators &#8211; another way to create properties</h2><p>We can create properties using decorators. This makes the definitions easier to read. Decorators are a ubiquitous feature of Python syntax, with a variety of purposes. For the most part, decorators modify the function definition that they precede. We'll look at the decorator design pattern more broadly in <em>Chapter 11</em>, <em>Common Design Patterns</em>.</p><p>The <code>property</code> function can be used with the decorator syntax to turn a <code>get</code> method into a <code>property</code> attribute, as follows:</p><pre><code><code>class NorwegianBlue_P:
    def __init__(self, name: str) -&gt; None:
        self._name = name
        self._state: str
    @property
    def silly(self) -&gt; str:
        print(f"Getting {self._name}'s State")
        return self._state</code></code></pre><p>This applies the <code>property</code> function as a decorator to the function that follows. It is equivalent to the previous <code>silly = property(_get_state)</code> syntax. The main difference, from a readability perspective, is that we get to mark the <code>silly</code> method as a property at the top of the method, instead of after it is defined, where it can be easily overlooked. It also means we don't have to create private methods with underscore prefixes just to define a property.</p><p>Going one step further, we can specify a <code>setter</code> function for the new property as follows:</p><pre><code><code>class NorwegianBlue_P:
    def __init__(self, name: str) -&gt; None:
        self._name = name
        self._state: str
    @property
    def silly(self) -&gt; str:
        """This is a silly property"""
        print(f"Getting {self._name}'s State")
        return self._state
    @silly.setter
    def silly(self, state: str) -&gt; None:
        print(f"Setting {self._name}'s State to {state!r}")
        self._state = state</code></code></pre><p>This syntax, <code>@silly.setter</code>, looks odd compared with <code>@property</code>, although the intent should be clear. First, we decorate the <code>silly</code> method as a getter. Then, we decorate a second method with exactly the same name by applying the <code>setter</code> attribute of the originally decorated <code>silly</code> method! This works because the <code>property</code> function returns an object; this object also has its own <code>setter</code> attribute, which can then be applied as a decorator to other methods. Using the same name for the get and set methods helps to group together the multiple methods that access one common attribute.</p><p>We can also specify a <code>delete</code> function with <code>@silly.deleter</code>. Here's what it looks like:</p><pre><code><code>@silly.deleter
def silly(self) -&gt; None:
    print(f"{self._name} is pushing up daisies!")
    del self._state</code></code></pre><p>We cannot specify a docstring using <code>property</code> decorators, so we need to rely on the decorator copying the docstring from the initial getter method. This class operates <em>exactly</em> the same as our earlier version, including the help text. You'll see the decorator syntax in widespread use. The function syntax is how it actually works under the hood.</p><h2>Deciding when to use properties</h2><p>With the built-in <code>property</code> blurring the division between behavior and data, it can be confusing to know when to choose an attribute, or a method, or a property. In the <code>Color_VP</code> class example we saw earlier, we added argument value validation to setting an attribute. In the <code>NorwegianBlue</code> class example, we wrote detailed log entries when attributes were set and deleted. There are also other factors to take into account when deciding to use a property.</p><p>In Python, data, properties, and methods are all attributes of a class. The fact that a method is callable does not distinguish it from other types of attributes; indeed, we'll see in <em>Chapter 8</em>, <em>The Intersection of Object-Oriented and Functional Programming</em>, that it is possible to create normal objects that can be called like functions. We'll also discover that functions and methods are themselves ordinary objects.</p><p>The fact that methods are callable attributes, and properties are also attributes, can help us make this decision. We suggest the following principles:</p><ul><li><p>Use methods to represent actions; things that can be done to, or performed by, the object. When you call a method, even with only one argument, it should <em>do</em> something. Method names are generally verbs.</p></li><li><p>Use attributes or properties to represent the state of the object. These are the nouns, adjectives, and prepositions that describe an object.</p><ul><li><p>Default to ordinary (non-property) attributes, initialized in the <code>__init__()</code> method. These must be computed eagerly, which is a good starting point for any design.</p></li><li><p>Use properties for attributes in the exceptional case when there's a computation involved with setting or getting (or deleting) an attribute. Examples include data validation, logging, and access controls. We'll look at cache management in a moment. We can also use properties for lazy attributes, where we want to defer the computation because it's costly and rarely needed.</p></li></ul></li></ul><p>Let's look at a more realistic example. A common need for custom behavior is caching a value that is difficult to calculate or expensive to look up (requiring, for example, a network request or database query). The goal is to store the value locally to avoid repeated calls to the expensive calculation.</p><p>We can do this with a custom getter on the property. The first time the value is retrieved, we perform the lookup or calculation. Then, we can locally cache the value as a private attribute on our object (or in dedicated caching software), and the next time the value is requested, we return the stored data. Here's how we might cache a web page:</p><pre><code><code>from urllib.request import urlopen
from typing import Optional, cast
class WebPage:
    def __init__(self, url: str) -&gt; None:
        self.url = url
        self._content: Optional[bytes] = None
    @property
    def content(self) -&gt; bytes:
        if self._content is None:
            print("Retrieving New Page...")
            with urlopen(self.url) as response:
                self._content = response.read()
        return self._content</code></code></pre><p>We'll only read the website content once, when <code>self._content</code> has the initial value of <code>None</code>. After that, we'll return the value most recently read for the site. We can test this code to see that the page is only retrieved once:</p><pre><code><code>import time
webpage = WebPage("http://ccphillips.net/")
now = time.perf_counter()
content1 = webpage.content
first_fetch = time.perf_counter() - now
now = time.perf_counter()
content2 = webpage.content
second_fetch = time.perf_counter() - now
assert content2 == content1, "Problem: Pages were different"
print(f"Initial Request     {first_fetch:.5f}")
print(f"Subsequent Requests {second_fetch:.5f}")</code></code></pre><p>The output?</p><pre><code><code>% python src/colors.py
Retrieving New Page...
Initial Request     1.38836
Subsequent Requests 0.00001</code></code></pre><p>It took about 1.388 seconds to retrieve a page from the <code>ccphilips.net</code> web host. The second fetch &#8211; from a laptop's RAM &#8211; takes 0.01 milliseconds! This is sometimes written as 10 &#956;s, 10 microseconds. Since this is the last digit, we can suspect it's subject to rounding, and the time may be only half that, perhaps as little as 5 &#956;s.</p><p>Custom getters are also useful for attributes that need to be calculated on the fly, based on other object attributes. For example, we might want to calculate the average for a list of integers:</p><pre><code><code>class AverageList(List[int]):
    @property
    def average(self) -&gt; float:
        return sum(self) / len(self)</code></code></pre><p>This small class inherits from <code>list</code>, so we get list-like behavior for free. We added a property to the class, and &#8211; hey, presto! &#8211; our list can have an average as follows:</p><pre><code><code>&gt;&gt;&gt; a = AverageList([10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5])
&gt;&gt;&gt; a.average
9.0</code></code></pre><p>Of course, we could have made this a method instead, but if we do, then we ought to call it <code>calculate_average()</code>, since methods represent actions. But a property called <code>average</code> is more suitable, and is both easier to type and easier to read.</p><p>We can imagine a number of similar reductions, including minimum, maximum, standard deviation, median, and mode, all being properties of a collection of numbers. This can simplify a more complex analysis by encapsulating these summaries into the collection of data values.</p><p>Custom setters are useful for validation, as we've already seen, but they can also be used to proxy a value to another location. For example, we could add a content setter to the <code>WebPage</code> class that automatically logs into our web server and uploads a new page whenever the value is set.</p><div><hr></div><h1>Manager objects</h1><p>We've been focused on objects and their attributes and methods. Now, we'll take a look at designing higher-level objects; the kind of objects that manage other objects &#8211; the objects that tie everything together. These are sometimes called Fa&#231;ade objects because they present a pleasant, easy-to-use fa&#231;ade over some underlying complexity. See <em>Chapter 12</em>, <em>Advanced Design Patterns</em>, for an additional look at the Fa&#231;ade design pattern.</p><p>Most of the previous examples tend to model concrete ideas. Management objects are more like office managers; they don't do the actual visible work out on the floor, but without them, there would be no communication between departments, and nobody would know what they are supposed to do (although, this can be true anyway if the organization is badly managed!). Analogously, the attributes on a management class tend to refer to other objects that do the visible work; the behaviors on such a class delegate to those other classes at the right time, and pass messages between them.</p><p>A manager relies on composite design. We assemble a manager class by knitting other objects together. The overall behavior of the manager emerges from the interaction of objects. To an extent, a manager is also an Adapter among the various interfaces. See <em>Chapter 12</em>, <em>Advanced Design Patterns</em>, for an additional look at the Adapter design pattern.</p><p>As an example, we'll write a program that does a find-and-replace action for text files stored in a compressed archive file, either a ZIP archive or a TAR archive. We'll need objects to represent the archive file as a whole and each individual text file (luckily, we don't have to write these classes, as they're available in the Python standard library).</p><p>An overall manager object will be responsible for ensuring the following three steps occur:</p><ol><li><p>Unzipping the compressed file to examine each member</p></li><li><p>Performing the find-and-replace action on text members</p></li><li><p>Zipping up the new files with the untouched as well as changed members</p></li></ol><p>Note that we have to choose between an eager and a lazy approach to the three steps of this process. We can eagerly unzip (or untar) the entire archive, process all the files, and then build a new archive. This tends to use a lot of disk space. An alternative is to lazily extract items one at a time from the archive, perform the find-and-replace, and then build a new compressed archive as we go. The lazy approach doesn't require as much storage.</p><p>This design will knit together elements of the <code>pathlib</code>, <code>zipfile</code>, and the regular expression (<code>re</code>) module. The initial design will be focused on the job at hand. Later in this chapter, we'll rethink this design as new requirements surface.</p><p>The class is initialized with the archive file's name. We don't do anything else upon creation. We'll define a method with a good, clear verb in its name that does any processing:</p><pre><code><code>from __future__ import annotations
import fnmatch
from pathlib import Path
import re
import zipfile
class ZipReplace:
    def __init__(
            self,
            archive: Path,
            pattern: str,
            find: str,
            replace: str
    ) -&gt; None:
        self.archive_path = archive
        self.pattern = pattern
        self.find = find
        self.replace = replace</code></code></pre><p>Given the archive, the filename pattern to match, and the strings to work with, the object will have everything it needs. We might provide arguments like <code>ZipReplace(Path("sample.zip"), "*.md", "xyzzy", "xyzzy")</code>.</p><p>The overall manager method for the find-and-replace operation revises a given archive. This method of the <code>ZipReplace</code> class (started above) uses two other methods and delegates most of the real work to other objects:</p><pre><code><code>    def find_and_replace(self) -&gt; None:
        input_path, output_path = self.make_backup()
        with zipfile.ZipFile(output_path, "w") as output:
            with zipfile.ZipFile(input_path) as input:
                self.copy_and_transform(input, output)</code></code></pre><p>The <code>make_backup()</code> method will use the <code>pathlib</code> module to rename the old ZIP file so it's obviously the backup copy, untouched. This backup copy is input to the <code>copy_and_transform()</code> method. The original name will be the final output, also. This makes it look like the file was updated "in place." In fact, a new file was created, but the old name will be assigned to the new content.</p><p>We create two context managers (a special kind of manager) to control the open files. An open file is entangled with operating system resources. In the case of a ZIP file or TAR archive, there are summaries and checksums that need to be properly written when the file is closed. Using a context manager assures that this additional work is done, and done properly, in spite of any exceptions being raised. All file operations should be wrapped in a <code>with</code> statement to leverage Python's context manager and handle proper cleanup. We'll look at this again in <em>Chapter 9</em>, <em>Strings, Serialization, and File Paths</em>.</p><p>The <code>copy_and_transform()</code> method uses methods of the two <code>ZipFile</code> instances and the <code>re</code> module to transform members of the original file. Since a backup was made of the original file, this will build the output file from the backup file. It examines each member of the archive, performing a number of steps, including expanding the compressed data, doing the transformation with a <code>transform()</code> method, and compressing to write to the output file, and then cleaning up the temporary file (and directories).</p><p>Obviously, we could do all of these steps in one method of a class, or indeed do the whole thing in one complex script, without ever creating an object. There are several advantages to separating the steps:</p><ul><li><p><strong>Readability</strong>: The code for each step is in a self-contained unit that is easy to read and understand. The method name describes what the method does, and less additional documentation is required to understand what is going on.</p></li><li><p><strong>Extensibility</strong>: If a subclass wanted to use compressed TAR files instead of ZIP files, it could override the <code>copy_and_transform()</code> method, reusing all the supporting methods because they apply to any file irrespective of the kind of archive.</p></li><li><p><strong>Partitioning</strong>: An external class could create an instance of this class and use <code>make_backup()</code> or the <code>copy_and_transform()</code> methods directly, bypassing the <code>find_and_replace()</code> manager.</p></li></ul><p>These two methods of the <code>ZipReplace</code> class (started above) make the backup copy and create the new file by reading from the backup and writing new items after they've been modified:</p><pre><code><code>def make_backup(self) -&gt; tuple[Path, Path]:
    input_path = self.archive_path.with_suffix(
        f"{self.archive_path.suffix}.old")
    output_path = self.archive_path
    self.archive_path.rename(input_path)
    return input_path, output_path
def copy_and_transform(
    self, input: zipfile.ZipFile, output: zipfile.ZipFile
) -&gt; None:
    for item in input.infolist():
        extracted = Path(input.extract(item))
        if (not item.is_dir() 
                and fnmatch.fnmatch(item.filename, self.pattern)):
            print(f"Transform {item}")
            input_text = extracted.read_text()
            output_text = re.sub(self.find, self.replace, input_text)
            extracted.write_text(output_text)
        else:
            print(f"Ignore    {item}")
        output.write(extracted, item.filename)
        extracted.unlink()
        for parent in extracted.parents:
            if parent == Path.cwd():
                break
            parent.rmdir()</code></code></pre><p>The <code>make_backup()</code> method applies a common strategy to avoid damaging a file. The original file is renamed to preserve it, and a new file is created that will have the original file's name. This method is designed to be independent of the file type or other processing details.</p><p>The <code>copy_and_transform()</code> function method builds the new archive out of members extracted from the original archive. It performs a number of steps for each member of the archive:</p><ul><li><p>Extract this file from the original archive.</p></li><li><p>If the item is not a directory (this is unlikely, but still possible), and the name matches the wild-card pattern, we want to transform it. This is a three-step process.</p><ol><li><p>Read the file's text.</p></li><li><p>Transform the file, using the <code>sub()</code> function of the <code>re</code> module.</p></li><li><p>Write the text, replacing the extracted file. This is where we create a copy of the content.</p></li></ol></li><li><p>Compress the file &#8211; either an untouched file or a transformed file &#8211; into the new archive.</p></li><li><p>We unlink the temporary copy. If there are no links left to the file, it will be deleted by the operating system.</p></li><li><p>We clean up any temporary directories created by the extraction process.</p></li><li><p>The <code>copy_and_transform()</code> method's operations span the <code>pathlib</code>, <code>zipfile</code>, and <code>re</code> modules. Wrapping these operations up into a manager that uses context managers gives us a tidy package with a small interface.</p></li></ul><p>We can create a main script to use the <code>ZipReplace</code> class:</p><pre><code><code>if __name__ == "__main__":
    sample_zip = Path("sample.zip")
    zr = ZipReplace(sample_zip, "*.md", "xyzzy", "plover's egg")
    zr.find_and_replace()</code></code></pre><p>We've provided the archive (<code>sample.zip</code>), the file matching pattern (<code>*.md</code>), the string to replace (<code>xyzzy</code>), and the final replacement (<code>plover's egg</code>). This will perform a complex series of file operations. A more practical approach is to use the <code>argparse</code> module to define the <strong>command-line interface</strong> (<strong>CLI</strong>) for this application.</p><p>For brevity, the details are sparsely documented. Our current focus is on object-oriented design; if you are interested in the inner details of the <code>zipfile</code> module, refer to the documentation in the standard library, either online or by typing <code>import zipfile</code> and <code>help(zipfile)</code> into your interactive interpreter.</p><p>Of course, an instance of the <code>ZipReplace</code> class does not have to be created from the command line; our class could be imported into another module (to perform batch ZIP file processing), or accessed as part of a GUI interface or even a higher-level manager object that knows where to get ZIP files (for example, to retrieve them from an FTP server or back them up to an external disk).</p><p>The benefit of the Fa&#231;ade and Adapter design patterns is to encapsulate complexity into a more useful class design. These composite objects tend to be less like physical objects, and enter the realm of conceptual objects. When we step away from objects that have a close parallel with the real world, the methods are actions that change the state of those concepts; care is required because the simple analogies start to disappear in the haze of ideas. It helps when the foundation is a set of concrete data values and well-defined behaviors.</p><p>A good example to bear in mind is the World Wide Web. A <em>web server</em> provides <em>content</em> to <em>browsers</em>. The content can include JavaScript that behaves like a desktop application which reaches out to other web servers to present content. These conceptual relationships are implemented by tangible transfers of bytes. It also includes a browser to paint pages of text, images, video, or sound. The foundation is transfers of bytes, a tangible action. In a classroom setting, it's possible to have developers pass sticky notes and rubber balls to each other to represent requests and responses.</p><p>This example works nicely. When we're confronted with additional requirements, we need to find a way to build new, related features without duplicating code. We'll talk about this engineering imperative first, then look at the revised design.</p><h2>Removing duplicate code</h2><p>Often, the code in management-style classes such as <code>ZipReplace</code> is quite generic and can be applied in a variety of ways. It is possible to use either composition or inheritance to help keep this code in one place, thus eliminating duplicate code. Before we look at any examples of this, let's discuss some design principles. Specifically, why is duplicate code a bad thing?</p><p>There are several reasons, but they all boil down to readability and maintainability. When we're writing a new piece of code that is similar to an earlier piece, the easiest thing to do is copy and paste the old code and change whatever needs to be changed (variable names, logic, comments) to make it work in the new location. Alternatively, if we're writing new code that seems similar, but not identical, to code elsewhere in the project, it is often easier to write fresh code with similar behavior, rather than figuring out how to extract the overlapping functionality. We sometimes call this copy-pasta programming because the result is a big mass of tangled noodles of code, like a bowl of spaghetti.</p><p>But as soon as someone trying to understand the code comes across duplicate (or nearly duplicate) code blocks, they now have an <em>additional</em> barrier to understanding. There's an intellectual friction created by a number of side-bar questions. Are they truly identical? If not, how is one section different from the other? What parts are the same? Under what conditions is one section called? When do we call the other? You might argue that you're the only one reading your code, but if you don't touch that code for eight months, it will be as incomprehensible to you as it is to a fresh coder. When we're trying to read two similar pieces of code, we have to understand why they're different, as well as how they're different. This wastes the reader's time; code should always be written to be readable first.</p><blockquote><p><em>[Dusty here, stepping out of formal author mode] I once had to try to understand someone's code that had three identical copies of the same 300 lines of very poorly written code. I had been working with the code for a month before I finally comprehended that the three identical versions were actually performing slightly different tax calculations. Some of the subtle differences were intentional, but there were also obvious areas where someone had updated a calculation in one function without updating the other two. The number of subtle, incomprehensible bugs in the code could not be counted. I eventually replaced all 900 lines with an easy-to-read function of 20 lines or so.</em></p></blockquote><p>As the preceding story suggests, keeping two similar pieces of code up to date can be a nightmare. We have to remember to update both sections whenever we update one of them, and we have to remember how multiple sections differ so we can modify our changes when we are editing each of them. If we forget to update all sections, we will end up with extremely annoying bugs that usually manifest themselves as: "<em>But I fixed that already, why is it still happening?</em>"</p><p>The key factor here is the time spent in troubleshooting, maintenance, and enhancement compared with the time spent initially creating the code. Software that's in use for more than a few weeks will have a lot more eyeballs on it than the time spent creating it. The tiny bit of time we "save" by copying and pasting existing code is more than wasted when we have to maintain it.</p><p>One of the author's personal bests was an application that was in use for almost seventeen years. If other developers and users wasted one extra day each year trying to sort out some confusing part of the code, it means the author should have spent at least two more weeks improving the code to head off this future maintenance cost.</p><p>Code is both read and modified many more times and much more often than it is written. Comprehensible code should always be a priority.</p><p>This is why programmers, especially Python programmers (who tend to value elegant code more than average developers), follow what is known as the <strong>Don't Repeat Yourself</strong> (<strong>DRY</strong>) principle. Our advice for beginner programmers is to never use the copy-and-paste feature of their editor. To intermediate programmers: think thrice before hitting Ctrl + C.</p><p>But what should we do instead of code duplication? The simplest solution is often to move the code into a function that accepts parameters to account for whatever parts are different. This isn't a strictly object-oriented solution, but it is frequently optimal.</p><p>For example, if we have two pieces of code that unzip a ZIP file into two different directories, we can easily replace it with a function that accepts a parameter for the directory to which it should be unzipped. This may make the function itself slightly longer. The size of a function &#8211; measured as lines of code &#8211; isn't a good metric for readability. No one wins at code golf.</p><p>Good names and docstrings are essential. Each class, method, function, variable, property, attribute, module, and package name should be chosen thoughtfully. When writing docstrings, don't explain how the code works (the code should do that). Be sure to focus on what the code's purpose is, what the preconditions are for using it, and what will be true after the function or method has been used.</p><p>The moral of the story is: always make the effort to refactor your code to be easier to read, instead of writing bad code that may seem easier to write. Now we can look at the revised design to the <code>ZipReplace</code> class definition.</p><h2>In practice</h2><p>Let's explore two ways we can reuse our existing code. After writing our code to replace strings in a ZIP file full of text files, we are later contracted to scale all the images in a ZIP file to a size suitable for mobile devices. While resolutions vary, 640 x 960 is about the smallest we need. It looks like we could use a very similar paradigm to what we used in <code>ZipReplace</code>.</p><p>Our first impulse might be to save a copy of that module and change the <code>find_replace</code> method to <code>scale_image</code> or something similar in the copy.</p><p>This processing will rely on the Pillow library to open an image file, scale it, and save it. The Pillow image processing tools can be installed with the following command:</p><pre><code><code>% python -m pip install pillow</code></code></pre><p>This will provide some great image-processing tools.</p><p>As we noted above in the <em>Removing duplicate code</em> section of this chapter, this copy-and-paste programming approach is suboptimal. What if someday we want to change the <code>unzip</code> and <code>zip</code> methods to also open TAR files? Or maybe we'll want to use a guaranteed unique directory name for temporary files. In either case, we'd have to change it in two different places!</p><p>We'll start by demonstrating an inheritance-based solution to this problem. First, we'll modify our original <code>ZipReplace</code> class into a superclass for processing ZIP files in a variety of ways:</p><pre><code><code>from abc import ABC, abstractmethod
class ZipProcessor(ABC):
    def __init__(self, archive: Path) -&gt; None:
        self.archive_path = archive
        self._pattern: str
    def process_files(self, pattern: str) -&gt; None:
        self._pattern = pattern
        input_path, output_path = self.make_backup()
        with zipfile.ZipFile(output_path, "w") as output:
            with zipfile.ZipFile(input_path) as input:
                self.copy_and_transform(input, output)
    def make_backup(self) -&gt; tuple[Path, Path]:
        input_path = self.archive_path.with_suffix(
            f"{self.archive_path.suffix}.old")
        output_path = self.archive_path
        self.archive_path.rename(input_path)
        return input_path, output_path
    def copy_and_transform(
        self, input: zipfile.ZipFile, output: zipfile.ZipFile
    ) -&gt; None:
        for item in input.infolist():
            extracted = Path(input.extract(item))
            if self.matches(item):
                print(f"Transform {item}")
                self.transform(extracted)
            else:
                print(f"Ignore    {item}")
            output.write(extracted, item.filename)
            self.remove_under_cwd(extracted)
    def matches(self, item: zipfile.ZipInfo) -&gt; bool:
        return (
            not item.is_dir() 
            and fnmatch.fnmatch(item.filename, self._pattern))
    def remove_under_cwd(self, extracted: Path) -&gt; None:
        extracted.unlink()
        for parent in extracted.parents:
            if parent == Path.cwd():
                break
            parent.rmdir()
    @abstractmethod
    def transform(self, extracted: Path) -&gt; None:
        ...</code></code></pre><p>We dropped the three parameters to <code>__init__()</code>, <code>pattern</code>, <code>find</code>, and <code>replace</code>, that were specific to <code>ZipReplace</code>. Then, we renamed the <code>find_replace()</code> method to <code>process_files()</code>. We decomposed the complex <code>copy_and_transform()</code> method and made it call several other methods to do the real work. This includes a placeholder for a <code>transform()</code> method. These name changes help demonstrate the more generalized nature of our new class.</p><p>This new <code>ZipProcessor</code> class is a subclass of <code>ABC</code>, an abstract base class, allowing us to provide placeholders instead of methods. (More on ABCs to come in <em>Chapter 6</em>, <em>Abstract Base Classes and Operator Overloading</em>.) This abstract class doesn't actually define a <code>transform()</code> method. If we try to create an instance of the <code>ZipProcessor</code> class, the missing <code>transform()</code> method will raise an exception. The formality of an <code>@abstractmethod</code> decoration makes it clear that there's a piece missing, and the piece must have the expected shape.</p><p>Now, before we move on to our image processing application, let's create a version of our original <code>ZipReplace</code> class. This will be based on the <code>ZipProcessor</code> class to make use of this parent class, as follows:</p><pre><code><code>class TextTweaker(ZipProcessor):
    def __init__(self, archive: Path) -&gt; None:
        super().__init__(archive)
        self.find: str
        self.replace: str
    def find_and_replace(self, find: str, replace: str) -&gt; "TextTweaker":
        self.find = find
        self.replace = replace
        return self
    def transform(self, extracted: Path) -&gt; None:
        input_text = extracted.read_text()
        output_text = re.sub(self.find, self.replace, input_text)
        extracted.write_text(output_text)</code></code></pre><p>This code is shorter than the original version, since it inherits its ZIP processing abilities from the parent class. We first import the base class we just wrote and make <code>TextTweaker</code> extend that class. Then, we use <code>super()</code> to initialize the parent class.</p><p>We need two extra parameters, and we've used a technique called a <em>fluent interface</em> to provide the two parameters. The <code>find_and_replace()</code> method updates the state of the object, then returns the <code>self</code> object. This lets us use the class with a line of code like the following:</p><pre><code><code>TextTweaker(zip_data)\
.find_and_replace("xyzzy", "plover's egg")\
.process_files("*.md")</code></code></pre><p>We've created an instance of the class, used the <code>find_and_replace()</code> method to set some of the attributes, then used the <code>process_files()</code> method to start the processing. This is called a "fluent" interface because a number of methods are used to help clarify the parameters and their relationships.</p><p>We've done a fair amount of work to recreate a program that is functionally not different from the one we started with! But having done that work, it is now much easier for us to write other classes that operate on files in a ZIP archive, such as the (hypothetically requested) photo scaler.</p><p>Further, if we ever want to improve or bug fix the ZIP functionality, we can do it for all subclasses at once by changing only the one <code>ZipProcessor</code> base class. Therefore maintenance will be much more effective.</p><p>See how simple it is now to create a photo scaling class that takes advantage of the <code>ZipProcessor</code> functionality:</p><pre><code><code>from PIL import Image  # type: ignore [import]
class ImgTweaker(ZipProcessor):
    def transform(self, extracted: Path) -&gt; None:
        image = Image.open(extracted)
        scaled = image.resize(size=(640, 960))
        scaled.save(extracted)</code></code></pre><p>Look how simple this class is! All that work we did earlier paid off. All we do is open each file, scale it, and save it back. The <code>ZipProcessor</code> class takes care of the zipping and unzipping without any extra work on our part. This seems to be a huge win.</p><p>Creating reusable code isn't easy. It generally requires more than one use case to make it clear what parts are generic and what parts are specific. Because we need concrete examples, it pays to avoid over-engineering to strive for imagined reuse. This is Python and things can be very flexible. Rewrite as needed to cover the cases as they arrive on the scene.</p><div><hr></div><h1>Case study</h1><p>In this chapter, we'll continue developing elements of the case study. We want to explore some additional features of object-oriented design in Python. The first is what is sometimes called "syntactic sugar," a handy way to write something that offers a simpler way to express something fairly complex. The second is the concept of a manager for providing a context for resource management.</p><p>In <em>Chapter 4</em>, <em>Expecting the Unexpected</em>, we built an exception for identifying invalid input data. We used the exception for reporting when the inputs couldn't be used.</p><p>Here, we'll start with a class to gather data by reading the file with properly classified training and test data. In this chapter, we'll ignore some of the exception-handling details so we can focus on another aspect of the problem: partitioning samples into testing and training subsets.</p><h2>Input validation</h2><p>The <code>TrainingData</code> object is loaded from a source file of samples, named <code>bezdekIris.data</code>. Currently, we don't make a large effort to validate the contents of this file. Rather than confirm the data contains correctly formatted samples with numeric measurements and a proper species name, we simply create <code>Sample</code> instances, and hope nothing goes wrong. A small change to the data could lead to unexpected problems in obscure parts of our application. By validating the input data right away, we can focus on the problems and provide a focused, actionable report back to the user. Something like "Row 42 has an invalid petal_length value of '1b.25'" with the line of data, the column, and the invalid value.</p><p>A file with training data is processed in our application via the <code>load()</code> method of <code>TrainingData</code>. Currently, this method requires an iterable sequence of dictionaries; each individual sample is read as a dictionary with the measurements and the classification. The type hint is <code>Iterable[dict[str, str]]</code>. This is one way the <code>csv</code> module works, making it very easy to work with. We'll return to additional details of loading the data in <em>Chapter 8</em>, <em>The Intersection of Object-Oriented and Functional Programming</em>, and <em>Chapter 9</em>, <em>Strings, Serialization, and File Paths</em>.</p><p>Thinking about the possibility of alternative formats suggests the <code>TrainingData</code> class should not depend on the <code>dict[str, str]</code> row definition suggested by CSV file processing. While expecting a dictionary of values for each row is simple, it pushes some details into the <code>TrainingData</code> class that may not belong there. Details of the source document's representation have nothing to do with managing a collection of training and test samples; this seems like a place where object-oriented design will help us disentangle the two ideas.</p><p>In order to support multiple sources of data, we will need some common rules for validating the input values. We'll need a class like this:</p><pre><code><code>class SampleReader:
    """
    See iris.names for attribute ordering in bezdekIris.data file
    """
    target_class = Sample
    header = [
        "sepal_length", "sepal_width", 
        "petal_length", "petal_width", "class"
    ]
    def __init__(self, source: Path) -&gt; None:
        self.source = source
    def sample_iter(self) -&gt; Iterator[Sample]:
        target_class = self.target_class
        with self.source.open() as source_file:
            reader = csv.DictReader(source_file, self.header)
            for row in reader:
                try:
                    sample = target_class(
                        sepal_length=float(row["sepal_length"]),
                        sepal_width=float(row["sepal_width"]),
                        petal_length=float(row["petal_length"]),
                        petal_width=float(row["petal_width"]),
                    )
                except ValueError as ex:
                    raise BadSampleRow(f"Invalid {row!r}") from ex
                yield sample</code></code></pre><p>This builds an instance of the <code>Sample</code> superclass from the input fields read by a CSV <code>DictReader</code> instance. The <code>sample_iter()</code> method uses a series of conversion expressions to translate input data from each column into useful Python objects. In this example, the conversions are simple, and the implementation is a bunch of <code>float()</code> functions to convert CSV string data into Python objects. We can imagine more complex conversions might be present for other problem domains.</p><p>The <code>float()</code> functions &#8211; when confronted with bad data &#8211; will raise a <code>ValueError</code>. While this is helpful, a bug in a distance formula may also raise a <code>ValueError</code>, leading to possible confusion. It's slightly better for our application to produce unique exceptions; this makes it easier to identify a root cause for a problem.</p><p>The target type, <code>Sample</code>, is provided as a class-level variable, <code>target_class</code>. This lets us introduce a new subclass of <code>Sample</code> by making one relatively visible change. This isn't required, but a visible dependency like this provides a way to disentangle classes from each other.</p><p>We'll follow <em>Chapter 4</em>, <em>Expecting the Unexpected</em>, and define a unique exception definition. This is a better way to help disentangle our application's errors from ordinary bugs in our Python code:</p><pre><code><code>class BadSampleRow(ValueError):
    pass</code></code></pre><p>To make use of this, we mapped the various <code>float()</code> problems signaled by <code>ValueError</code> exceptions to our application's <code>BadSampleRow</code> exception. This can help someone distinguish between a bad CSV source file and a bad computation due to a bug in a <em>k</em>-NN distance computation. While both can raise <code>ValueError</code> exceptions, the CSV processing exception is wrapped into an application-specific exception to disambiguate the context.</p><p>We've done the exception transform by wrapping the creation of an instance of the target class in a <code>try:</code> statement. Any <code>ValueError</code> that's raised here will become a <code>BadSampleRow</code> exception. We've used a <code>raise...from...</code> so that the original exception is preserved to help with the debugging.</p><p>Once we have valid input, we have to decide whether the object should be used for training or testing. We'll turn to that problem next.</p><h2>Input partitioning</h2><p>The <code>SampleReader</code> class we just introduced uses a variable to identify what kind of objects to create. The <code>target_class</code> variable provides a class to use. Note that we need to be a little careful in the ways we refer to <code>SampleReader.target_class</code> or <code>self.target_class</code>.</p><p>A simple expression like <code>self.target_class(sepal_length=, ... etc.)</code> looks like a method evaluation. Except, of course, <code>self.target_class</code> is not a method; it's another class. To make sure Python doesn't assume that <code>self.target_class()</code> refers to a method, we've assigned it to a local variable called <code>target_class</code>. Now we can use <code>target_class(sepal_length=, &#8230; etc.)</code> and there's no ambiguity.</p><p>This is pleasantly Pythonic. We can create subclasses of this reader to create different kinds of samples from the raw data.</p><p>This <code>SampleReader</code> class definition exposes a problem. A single source of raw sample data needs to be partitioned into two separate subclasses of <code>KnownSample</code>; it's either a <code>TrainingSample</code> or a <code>TestingSample</code>. There's a tiny difference in behavior between these two classes. A <code>TestingSample</code> is used to confirm the <em>k</em>-NN algorithm works, and is used to compare an algorithmic classification against the expert Botanist-assigned species. This is not something a <code>TrainingSample</code> needs to do.</p><p>Ideally, a single reader would emit a mixture of the two classes. The design so far only allows for instances of a single class to be created. We have two paths forward to provide the needed functionality:</p><ul><li><p>A more sophisticated algorithm for deciding what class to create. The algorithm would likely include an <code>if</code> statement to create an instance of one object or another.</p></li><li><p>A simplified definition of <code>KnownSample</code>. This single class can handle immutable training samples separately from mutable testing samples that can be classified (and reclassified) any number of times.</p></li></ul><p>Simplification seems to be a good idea. Less complexity means less code and fewer places for bugs to hide. The second alternative suggests we can separate three distinct aspects of a sample:</p><ul><li><p>The "raw" data. This is the core collection of measurements. They are immutable. (We'll address this design variation in <em>Chapter 7</em>, <em>Python Data Structures</em>.)</p></li><li><p>The Botanist-assigned species. This is available for training or testing data, but not part of an unknown sample. The assigned species, like the measurements, is immutable.</p></li><li><p>An algorithmically assigned classification. This is applied to the testing and unknown samples. This value can be seen as mutable; each time we classify a sample (or reclassify a test sample), the value changes.</p></li></ul><p>This a profound change to the design created so far. Early in a project, this kind of change can be necessary. Way back in <em>Chapters 1</em> and <em>2</em>, we decided to create a fairly sophisticated class hierarchy for various kinds of samples. It's time to revisit that design. This won't be the last time we think through this. The essence of good design is to create and dispose of a number of bad designs first.</p><h2>The sample class hierarchy</h2><p>We can rethink our earlier designs from several points of view. One alternative is to separate the essential <code>Sample</code> class from the additional features. It seems like we can identify four additional behaviors for each <code>Sample</code> instance, shown in the following table.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7iz5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb015a1f-f7c1-4cc9-b53b-9464eaf6f2f1_590x171.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7iz5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb015a1f-f7c1-4cc9-b53b-9464eaf6f2f1_590x171.png 424w, https://substackcdn.com/image/fetch/$s_!7iz5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb015a1f-f7c1-4cc9-b53b-9464eaf6f2f1_590x171.png 848w, https://substackcdn.com/image/fetch/$s_!7iz5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb015a1f-f7c1-4cc9-b53b-9464eaf6f2f1_590x171.png 1272w, https://substackcdn.com/image/fetch/$s_!7iz5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb015a1f-f7c1-4cc9-b53b-9464eaf6f2f1_590x171.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7iz5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb015a1f-f7c1-4cc9-b53b-9464eaf6f2f1_590x171.png" width="590" height="171" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fb015a1f-f7c1-4cc9-b53b-9464eaf6f2f1_590x171.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:171,&quot;width&quot;:590,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:17712,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/172067259?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb015a1f-f7c1-4cc9-b53b-9464eaf6f2f1_590x171.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7iz5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb015a1f-f7c1-4cc9-b53b-9464eaf6f2f1_590x171.png 424w, https://substackcdn.com/image/fetch/$s_!7iz5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb015a1f-f7c1-4cc9-b53b-9464eaf6f2f1_590x171.png 848w, https://substackcdn.com/image/fetch/$s_!7iz5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb015a1f-f7c1-4cc9-b53b-9464eaf6f2f1_590x171.png 1272w, https://substackcdn.com/image/fetch/$s_!7iz5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb015a1f-f7c1-4cc9-b53b-9464eaf6f2f1_590x171.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>We've omitted a detail from the <strong>Classified</strong> row. Each time we do a classification, a specific hyperparameter is associated with the resulting classified sample. It would be more accurate to say it's a sample classified by a specific <code>Hyperparameter</code> object. But this might be too much clutter.</p><p>The distinction between the two cells in the <strong>Unknown</strong> column is minute. The distinction is so minor as to be essentially irrelevant to most processing. An <strong>Unknown</strong> sample will be waiting to be classified for &#8211; at most &#8211; a few lines of code.</p><p>If we rethink this, we may be able to create fewer classes and still reflect the object state and behavior changes correctly.</p><p>There can be two subclasses of <code>Sample</code> with a separate <code>Classification</code> object. Here's a diagram.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Inh_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32144b6e-2602-46b7-a343-0bd4add34769_827x711.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Inh_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32144b6e-2602-46b7-a343-0bd4add34769_827x711.png 424w, https://substackcdn.com/image/fetch/$s_!Inh_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32144b6e-2602-46b7-a343-0bd4add34769_827x711.png 848w, https://substackcdn.com/image/fetch/$s_!Inh_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32144b6e-2602-46b7-a343-0bd4add34769_827x711.png 1272w, https://substackcdn.com/image/fetch/$s_!Inh_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32144b6e-2602-46b7-a343-0bd4add34769_827x711.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Inh_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32144b6e-2602-46b7-a343-0bd4add34769_827x711.png" width="827" height="711" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/32144b6e-2602-46b7-a343-0bd4add34769_827x711.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:711,&quot;width&quot;:827,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Diagram\n\nDescription automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Diagram

Description automatically generated" title="Diagram

Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!Inh_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32144b6e-2602-46b7-a343-0bd4add34769_827x711.png 424w, https://substackcdn.com/image/fetch/$s_!Inh_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32144b6e-2602-46b7-a343-0bd4add34769_827x711.png 848w, https://substackcdn.com/image/fetch/$s_!Inh_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32144b6e-2602-46b7-a343-0bd4add34769_827x711.png 1272w, https://substackcdn.com/image/fetch/$s_!Inh_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32144b6e-2602-46b7-a343-0bd4add34769_827x711.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 5.1: Sample class diagram</em></p><p>We've refined the class hierarchy to reflect two essentially different kinds of samples:</p><ul><li><p>A <code>KnownSample</code> instance can be used for testing or training. The difference between other classes is implemented in the method that does classification. We can make this depend on a <code>purpose</code> attribute, shown with a small square (or sometimes a "<code>-</code>") as a prefix. Python doesn't have private variables, but this marker can be helpful as a design note. The public attributes can be shown with a small circle (or a "<code>+</code>" to save space) as a prefix.</p><ul><li><p>When the purpose has a value of <code>Training</code>, the <code>classify()</code> method will raise an exception. The sample cannot be re-classified; that would invalidate the training.</p></li><li><p>When the purpose has a value of <code>Testing</code>, the <code>classify()</code> method will work normally, applying a given <code>Hyperparameter</code> to compute a species.</p></li></ul></li><li><p>An <code>UnknownSample</code> instance can be used for user classification. The classification method here does not depend on the value of the <code>purpose</code> attribute, and always performs classification.</p></li></ul><p>Let's look at implementing these behaviors with the <code>@property</code> decorator we learned about in this chapter. We can use <code>@property</code> to fetch computed values as if they were simple attributes. We can also use <code>@property</code> to define attributes that cannot be set.</p><h2>The purpose enumeration</h2><p>We'll start by enumerating a domain of <code>purpose</code> values:</p><pre><code><code>class Purpose(enum.IntEnum):
    Classification = 0
    Testing = 1
    Training = 2</code></code></pre><p>This definition creates a namespace with three objects we can use in our code: <code>Purpose.Classification</code>, <code>Purpose.Testing</code>, and <code>Purpose.Training</code>. For example, we can use <code>if sample.purpose == Purpose.Testing:</code> to identify a testing sample.</p><p>We can convert to <code>Purpose</code> objects from input values using <code>Purpose(x)</code> where <code>x</code> is an integer value, 0, 1, or 2. Any other value will raise a <code>ValueError</code> exception. We can convert back to numeric values, also. For example, <code>Purpose.Training.value</code> is <code>1</code>. This use of numeric codes can fit well with external software that doesn't deal well with an enumeration of Python objects.</p><p>We'll decompose the <code>KnownSample</code> subclass of the <code>Sample</code> class into two parts. Here's the first part. We initialize a sample with the data required by the <code>Sample.__init__()</code> method plus two additional values, the <code>purpose</code> numeric code, and the assigned species:</p><pre><code><code>class KnownSample(Sample):
    def __init__(
        self,
        sepal_length: float,
        sepal_width: float,
        petal_length: float,
        petal_width: float,
        purpose: int,
        species: str,
    ) -&gt; None:
        purpose_enum = Purpose(purpose)
        if purpose_enum not in {Purpose.Training, Purpose.Testing}:
            raise ValueError(
                f"Invalid purpose: {purpose!r}: {purpose_enum}"
            )
        super().__init__(
            sepal_length=sepal_length,
            sepal_width=sepal_width,
            petal_length=petal_length,
            petal_width=petal_width,
        )
        self.purpose = purpose_enum
        self.species = species
        self._classification: Optional[str] = None
    def matches(self) -&gt; bool:
        return self.species == self.classification</code></code></pre><p>We validate the <code>purpose</code> parameter's value to be sure it decodes to either <code>Purpose.Training</code> or <code>Purpose.Testing</code>. If the <code>purpose</code> value isn't one of the two allowed values, we'll raise a <code>ValueError</code> exception because the data is unusable.</p><p>We've created an instance variable, <code>self._classification</code>, with a leading <code>_</code> name. This is a convention that suggests the name is not for general use by clients of this class. It's not "private," since there's no notion of privacy in Python. We could call it "concealed" or perhaps "watch out for surprises here."</p><p>Instead of a large, opaque wall available in some languages, Python uses a low, decorative floral border that sets this variable apart from the others. You can march right through the floral <code>_</code> character to look at the value closely, but you probably shouldn't.</p><p>Here's the first <code>@property</code> method:</p><pre><code><code>    @property
    def classification(self) -&gt; Optional[str]:
        if self.purpose == Purpose.Testing:
            return self._classification
        else:
            raise AttributeError(f"Training samples have no classification")</code></code></pre><p>This defines a method that will be visible as an attribute name. Here's an example of creating a sample for testing purposes:</p><pre><code><code>&gt;&gt;&gt; from model import KnownSample, Purpose
&gt;&gt;&gt; s2 = KnownSample(
...     sepal_length=5.1, 
...     sepal_width=3.5, 
...     petal_length=1.4, 
...     petal_width=0.2, 
...     species="Iris-setosa", 
...     purpose=Purpose.Testing.value)
&gt;&gt;&gt; s2
KnownSample(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, purpose=1, species='Iris-setosa')
&gt;&gt;&gt; s2.classification is None
True</code></code></pre><p>When we evaluate <code>s2.classification</code>, this will call the method. This function makes sure this is a sample to be used for testing, and returns the value of the "concealed" instance variable <code>self._classification</code>.</p><p>If this is a <code>Purpose.Training</code> sample, the property will raise an <code>AttributeError</code> exception because any application that checks the value of the classification for a training sample has a bug in it that needs to be fixed.</p><h2>Property setters</h2><p>How do we set the classification? Do we really execute the statement <code>self._classification= h.classify(self)</code>? The answer is no &#8211; we can create a property that updates the "concealed" instance variable. This is a bit more complex than the example above:</p><pre><code><code>    @classification.setter
    def classification(self, value: str) -&gt; None:
        if self.purpose == Purpose.Testing:
            self._classification = value
        else:
            raise AttributeError(
                f"Training samples cannot be classified")</code></code></pre><p>The initial <code>@property</code> definition for <code>classification</code> is called a "getter." It gets the value of an attribute. (The implementation uses the <code>__get__()</code> method of a descriptor object that was created for us.) The <code>@property</code> definition for <code>classification</code> also creates an additional decorator, <code>@classification.setter</code>. The method decorated by the setter is used by assignment statements.</p><p>Note that the method names for these two properties are both <code>classification</code>. This is the attribute name to be used.</p><p>Now a statement like <code>s2.classification = h.classify(self)</code> will change the classification from a particular <code>Hyperparameter</code> object. This assignment statement will use the method to examine the purpose of this sample. If the purpose is testing, the value will be saved. If the purpose is not <code>Purpose.Testing</code>, then attempting to set a classification raises an <code>AttributeError</code> exception, and identifies a place where something's wrong in our application.</p><h2>Repeated if statements</h2><p>We have a number of <code>if</code> statements checking for specific <code>Purpose</code> values. This is a suggestion that this design is not optimal. The variant behavior is not encapsulated in a single class; instead, multiple behaviors are combined into a class.</p><p>The presence of a <code>Purpose</code> enumeration and <code>if</code> statements to check for the enumerated values is a suggestion that we have multiple classes. The "simplification" here isn't desirable.</p><p>In the <em>Input partitioning</em> section of this case study, we suggested there were two paths forward. One was to try and simplify the classes by setting the <code>purpose</code> attribute to separate testing from training data. This seems to have added <code>if</code> statements, without really simplifying the design.</p><p>This means we'll have to search for a better partitioning algorithm in a later chapter's case study. For now, we have the capability of creating valid data, but we also have code that's cluttered with <code>if</code> statements. The reader is encouraged to try alternative designs to examine the resulting code to see what seems simpler and easier to read.</p><div><hr></div><h1>Recall</h1><p>Here are some of the key points in this chapter:</p><ul><li><p>When we have both data and behavior, this is the sweet spot for object-oriented design. We can leverage Python's generic collections and ordinary functions for many things. When it becomes complex enough that we need to be sure that pieces are all defined together, then we need to start using classes.</p></li><li><p>When an attribute value is a reference to another object, the Pythonic approach is to allow direct access to the attribute; we don't write elaborate setter and getter functions. When an attribute value is computed, we have two choices: we can compute it eagerly or lazily. A property lets us be lazy and do the computation just in time.</p></li><li><p>We'll often have cooperating objects; the behavior of the application emerges from the cooperation. This can often lead to manager objects that combine behaviors from component class definitions to create an integrated, working whole.</p></li></ul><div><hr></div><h1>Exercises</h1><p>We've looked at various ways that objects, data, and methods can interact with each other in an object-oriented Python program. As usual, your first thoughts should be how you can apply these principles to your own work. Do you have any messy scripts lying around that could be rewritten using an object-oriented manager? Look through some of your old code and look for methods that are not actions. If the name isn't a verb, try rewriting it as a property.</p><p>Think about code you've written in any language. Does it break the DRY principle? Is there any duplicate code? Did you copy and paste code? Did you write two versions of similar pieces of code because you didn't feel like understanding the original code? Go back over some of your recent code now and see whether you can refactor the duplicate code using inheritance or composition. Try to pick a project you're still interested in maintaining, not code so old that you never want to touch it again. That will help to keep you interested when you do the improvements!</p><p>Now, look back over some of the examples we looked at in this chapter. Start with the cached web page example that uses a property to cache the retrieved data. An obvious problem with this example is that the cache is never refreshed. Add a timeout to the property's getter, and only return the cached page if the page has been requested before the timeout has expired. You can use the <code>time</code> module (<code>time.time() - an_old_time</code> returns the number of seconds that have elapsed since <code>an_old_time</code>) to determine whether the cache has expired.</p><p>Also look at the inheritance-based <code>ZipProcessor</code>. It might be reasonable to use composition instead of inheritance here. Instead of extending the class in the <code>ZipReplace</code> and <code>ScaleZip</code> classes, you could pass instances of those classes into the <code>ZipProcessor</code> constructor and call them to do the processing part. Implement this.</p><p>Which version do you find easier to use? Which is more elegant? What is easier to read? These are subjective questions; the answer varies for each of us. Knowing the answer, however, is important. If you find you prefer inheritance over composition, you need to pay attention that you don't overuse inheritance in your daily coding. If you prefer composition, make sure you don't miss opportunities to create an elegant inheritance-based solution.</p><p>Finally, add some error handlers to the various classes we created in the case study. How should one bad sample be handled? Should the model be inoperable? Or should the row be skipped? There are profound data science and statistical consequences to a seemingly small technical implementation choice. Can we define a class that permits either alternative behavior?</p><p>In your daily coding, pay attention to the copy and paste commands. Every time you use them in your editor, consider whether it would be a good idea to improve your program's organization so that you only have one version of the code you are about to copy.</p><div><hr></div><h1>Summary</h1><p>In this chapter, we focused on identifying objects, especially objects that are not immediately apparent; objects that manage and control. Objects should have both data and behaviors, but properties can be used to blur the distinction between the two. The DRY principle is an important indicator of code quality, and inheritance and composition can be applied to reduce code duplication.</p><p>In the next chapter, we'll look at Python's methods for defining abstract base classes. This lets us define a class that's a kind of template; it must be extended with subclasses that add narrowly-defined implementation features. This lets us build families of related classes, confident that they will work together properly.</p><div><hr></div><p><strong>About the Author:</strong></p><p><strong>Steven Lott</strong> has been programming since computers were large, expensive, and rare. Working for decades in high tech has given him exposure to a lot of ideas and techniques, some bad, but most are helpful to others. Since the 1990s, Steven has been engaged with Python, crafting an array of indispensable tools and applications. His profound expertise has led him to contribute significantly to Packt Publishing, penning notable titles like <em><strong><a href="https://www.packtpub.com/en-us/product/mastering-object-oriented-python-9781789531404">Mastering Object-Oriented Python</a> </strong></em>(Packt, 2019), <em><strong><a href="https://www.packtpub.com/en-us/product/modern-python-cookbook-9781835466384">Modern Python Cookbook</a></strong></em> (Packt, 2024), and <em><strong><a href="https://www.packtpub.com/en-us/product/python-real-world-projects-9781803246765">Python Real-World Projects</a> </strong></em>(Packt, 2023). A self-proclaimed technomad, Steven's unconventional lifestyle sees him residing on a boat, often anchored along the vibrant east coast of the US. He tries to live by the words &#8220;Don&#8217;t come home until you have a story.&#8221;</p><div><hr></div><p>Lott is currently working on the <strong>5th Ed.</strong> of <em><strong>Python Object-Oriented Programming</strong> </em>due later this year. If you liked this chapter and want more right now, you can check out <strong><a href="https://www.packtpub.com/en-us/product/python-object-oriented-programming-9781801075237">Python Object-Oriented Programming, 4th Ed. (Packt, 2021)</a>.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.packtpub.com/en-us/product/python-object-oriented-programming-9781801075237" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!S24_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0059565-ab59-4421-90dd-de189e63939f_810x1000 424w, https://substackcdn.com/image/fetch/$s_!S24_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0059565-ab59-4421-90dd-de189e63939f_810x1000 848w, https://substackcdn.com/image/fetch/$s_!S24_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0059565-ab59-4421-90dd-de189e63939f_810x1000 1272w, https://substackcdn.com/image/fetch/$s_!S24_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0059565-ab59-4421-90dd-de189e63939f_810x1000 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!S24_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0059565-ab59-4421-90dd-de189e63939f_810x1000" width="340" height="419.75308641975306" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b0059565-ab59-4421-90dd-de189e63939f_810x1000&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1000,&quot;width&quot;:810,&quot;resizeWidth&quot;:340,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Python Object-Oriented Programming&quot;,&quot;title&quot;:&quot;Python Object-Oriented Programming&quot;,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.packtpub.com/en-us/product/python-object-oriented-programming-9781801075237&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Python Object-Oriented Programming" title="Python Object-Oriented Programming" srcset="https://substackcdn.com/image/fetch/$s_!S24_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0059565-ab59-4421-90dd-de189e63939f_810x1000 424w, https://substackcdn.com/image/fetch/$s_!S24_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0059565-ab59-4421-90dd-de189e63939f_810x1000 848w, https://substackcdn.com/image/fetch/$s_!S24_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0059565-ab59-4421-90dd-de189e63939f_810x1000 1272w, https://substackcdn.com/image/fetch/$s_!S24_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0059565-ab59-4421-90dd-de189e63939f_810x1000 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here is what some readers have said:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!B_ZW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0076979-6368-4943-8ac8-1595f1bdb737_1078x432.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B_ZW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0076979-6368-4943-8ac8-1595f1bdb737_1078x432.png 424w, https://substackcdn.com/image/fetch/$s_!B_ZW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0076979-6368-4943-8ac8-1595f1bdb737_1078x432.png 848w, https://substackcdn.com/image/fetch/$s_!B_ZW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0076979-6368-4943-8ac8-1595f1bdb737_1078x432.png 1272w, https://substackcdn.com/image/fetch/$s_!B_ZW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0076979-6368-4943-8ac8-1595f1bdb737_1078x432.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B_ZW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0076979-6368-4943-8ac8-1595f1bdb737_1078x432.png" width="1078" height="432" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f0076979-6368-4943-8ac8-1595f1bdb737_1078x432.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:432,&quot;width&quot;:1078,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:95317,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/169634247?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0076979-6368-4943-8ac8-1595f1bdb737_1078x432.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!B_ZW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0076979-6368-4943-8ac8-1595f1bdb737_1078x432.png 424w, https://substackcdn.com/image/fetch/$s_!B_ZW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0076979-6368-4943-8ac8-1595f1bdb737_1078x432.png 848w, https://substackcdn.com/image/fetch/$s_!B_ZW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0076979-6368-4943-8ac8-1595f1bdb737_1078x432.png 1272w, https://substackcdn.com/image/fetch/$s_!B_ZW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0076979-6368-4943-8ac8-1595f1bdb737_1078x432.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QhcA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6d263b1-d57e-44df-bbb1-6d36e59a7807_1097x495.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QhcA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6d263b1-d57e-44df-bbb1-6d36e59a7807_1097x495.png 424w, https://substackcdn.com/image/fetch/$s_!QhcA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6d263b1-d57e-44df-bbb1-6d36e59a7807_1097x495.png 848w, https://substackcdn.com/image/fetch/$s_!QhcA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6d263b1-d57e-44df-bbb1-6d36e59a7807_1097x495.png 1272w, https://substackcdn.com/image/fetch/$s_!QhcA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6d263b1-d57e-44df-bbb1-6d36e59a7807_1097x495.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QhcA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6d263b1-d57e-44df-bbb1-6d36e59a7807_1097x495.png" width="1097" height="495" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c6d263b1-d57e-44df-bbb1-6d36e59a7807_1097x495.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:495,&quot;width&quot;:1097,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:120161,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/169634247?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6d263b1-d57e-44df-bbb1-6d36e59a7807_1097x495.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!QhcA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6d263b1-d57e-44df-bbb1-6d36e59a7807_1097x495.png 424w, https://substackcdn.com/image/fetch/$s_!QhcA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6d263b1-d57e-44df-bbb1-6d36e59a7807_1097x495.png 848w, https://substackcdn.com/image/fetch/$s_!QhcA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6d263b1-d57e-44df-bbb1-6d36e59a7807_1097x495.png 1272w, https://substackcdn.com/image/fetch/$s_!QhcA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6d263b1-d57e-44df-bbb1-6d36e59a7807_1097x495.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bMcY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9f8102a-90fa-4688-b30c-92128ca51d95_1070x365.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bMcY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9f8102a-90fa-4688-b30c-92128ca51d95_1070x365.png 424w, https://substackcdn.com/image/fetch/$s_!bMcY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9f8102a-90fa-4688-b30c-92128ca51d95_1070x365.png 848w, https://substackcdn.com/image/fetch/$s_!bMcY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9f8102a-90fa-4688-b30c-92128ca51d95_1070x365.png 1272w, https://substackcdn.com/image/fetch/$s_!bMcY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9f8102a-90fa-4688-b30c-92128ca51d95_1070x365.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bMcY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9f8102a-90fa-4688-b30c-92128ca51d95_1070x365.png" width="1070" height="365" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c9f8102a-90fa-4688-b30c-92128ca51d95_1070x365.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:365,&quot;width&quot;:1070,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:81939,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/169634247?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9f8102a-90fa-4688-b30c-92128ca51d95_1070x365.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!bMcY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9f8102a-90fa-4688-b30c-92128ca51d95_1070x365.png 424w, https://substackcdn.com/image/fetch/$s_!bMcY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9f8102a-90fa-4688-b30c-92128ca51d95_1070x365.png 848w, https://substackcdn.com/image/fetch/$s_!bMcY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9f8102a-90fa-4688-b30c-92128ca51d95_1070x365.png 1272w, https://substackcdn.com/image/fetch/$s_!bMcY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9f8102a-90fa-4688-b30c-92128ca51d95_1070x365.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Go Concurrency]]></title><description><![CDATA[The complete &#8220;Chapter 8: Go Concurrency&#8221; from the book Mastering Go, Fourth Edition by Mihalis Tsoukalos (Packt, March 2024).]]></description><link>https://deepengineering.substack.com/p/go-concurrency</link><guid isPermaLink="false">https://deepengineering.substack.com/p/go-concurrency</guid><dc:creator><![CDATA[Mihalis Tsoukalos]]></dc:creator><pubDate>Wed, 20 Aug 2025 10:47:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!VBef!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84ede4bc-d8fb-4c67-a46b-0855efef6779_2250x2775" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The key component of the Go concurrency model is the goroutine, which is the <em><strong>minimum executable entity</strong></em> in Go. To create a new goroutine, we must use the <code>go</code> keyword followed by a function call or an anonymous function&#8212;the two methods are equivalent. For a goroutine or a function to terminate the entire Go application, it should call <code>os.Exit()</code> instead of <code>return</code>. However, most of the time, we exit a goroutine or a function using <code>return</code> because what we really want is to exit that specific goroutine or function and not stop the entire application.</p><p>Everything in Go is executed as a goroutine, either transparently or consciously. Each executable Go program has at least one goroutine, which is used for running the <code>main()</code> function of the <code>main</code> package. Each goroutine is executed on a single OS thread according to the instructions of the Go scheduler, which is responsible for the execution of goroutines&#8212;the developer has no control over the amount of memory allocated to a goroutine. The OS scheduler does not dictate how many threads the Go runtime is going to create because the Go runtime will spawn enough threads to ensure that <code>GOMAXPROCS</code> threads are available to run the Go code.</p><p>However, <em><strong>goroutines cannot communicate with each other directly</strong></em>. Data sharing in Go is implemented using either channels, local sockets, or shared memory. <em>Channels</em> act as the glue that connects multiple goroutines. On the other hand, channels cannot process data or execute code but they can send data to and receive data from goroutines and have a special purpose like acting as signals or specifying the order of execution for goroutines.</p><p>When I first learned about channels, I thought that they were a great idea, much better than shared memory, and I wanted to use channels everywhere! However, nowadays<em><strong> I only use channels when I have no other alternative</strong></em>. Look at the implementation of the concurrent statistical application at the end of the chapter to realize that there exist designs that do not require the use of channels.</p><p>Although the use of channels to communicate and synchronize between goroutines is very typical and expected, channels might introduce deadlocks, overhead, and complexity to the design, as well as performance considerations, especially when low-latency communication is a priority.</p><p>When you combine multiple channels and goroutines, you can create data flows, which, in Go terminology, are called pipelines. So, you might have a goroutine that reads data from a database and sends it to a channel and a second goroutine that reads from that channel, processes that data, and sends it to another channel to be read from another goroutine, before making modifications to the data and storing it in another database.</p><p>This chapter covers:</p><ul><li><p>Processes, threads, and goroutines</p></li><li><p>The Go scheduler</p></li><li><p>Goroutines</p></li><li><p>Channels</p></li><li><p>Race conditions are bad</p></li><li><p>The <code>select</code> keyword</p></li><li><p>Timing out a goroutine</p></li><li><p>Go channels revisited</p></li><li><p>Handling UNIX signals</p></li><li><p>Shared memory and shared variables</p></li><li><p>Closured variables and the <code>go</code> statement</p></li><li><p>The <code>context</code> package</p></li><li><p>The <code>semaphore</code> package</p></li><li><p>Making the statistics application concurrent</p></li></ul><div><hr></div><h1>Go Concurrency</h1><p>The key component of the Go concurrency model is the goroutine, which is the <em><strong>minimum executable entity</strong></em> in Go. To create a new goroutine, we must use the <code>go</code> keyword followed by a function call or an anonymous function&#8212;the two methods are equivalent. For a goroutine or a function to terminate the entire Go application, it should call <code>os.Exit()</code> instead of <code>return</code>. However, most of the time, we exit a goroutine or a function using <code>return</code> because what we really want is to exit that specific goroutine or function and not stop the entire application.</p><p>Everything in Go is executed as a goroutine, either transparently or consciously. Each executable Go program has at least one goroutine, which is used for running the <code>main()</code> function of the <code>main</code> package. Each goroutine is executed on a single OS thread according to the instructions of the Go scheduler, which is responsible for the execution of goroutines&#8212;the developer has no control over the amount of memory allocated to a goroutine. The OS scheduler does not dictate how many threads the Go runtime is going to create because the Go runtime will spawn enough threads to ensure that <code>GOMAXPROCS</code> threads are available to run the Go code.</p><p>However, <em><strong>goroutines cannot communicate with each other directly</strong></em>. Data sharing in Go is implemented using either channels, local sockets, or shared memory. <em>Channels</em> act as the glue that connects multiple goroutines. On the other hand, channels cannot process data or execute code but they can send data to and receive data from goroutines and have a special purpose like acting as signals or specifying the order of execution for goroutines.</p><p>When I first learned about channels, I thought that they were a great idea, much better than shared memory, and I wanted to use channels everywhere! However, nowadays<em><strong> I only use channels when I have no other alternative</strong></em>. Look at the implementation of the concurrent statistical application at the end of the chapter to realize that there exist designs that do not require the use of channels.</p><p>Although the use of channels to communicate and synchronize between goroutines is very typical and expected, channels might introduce deadlocks, overhead, and complexity to the design, as well as performance considerations, especially when low-latency communication is a priority.</p><p>When you combine multiple channels and goroutines, you can create data flows, which, in Go terminology, are called pipelines. So, you might have a goroutine that reads data from a database and sends it to a channel and a second goroutine that reads from that channel, processes that data, and sends it to another channel to be read from another goroutine, before making modifications to the data and storing it in another database.</p><p>This chapter covers:</p><ul><li><p>Processes, threads, and goroutines</p></li><li><p>The Go scheduler</p></li><li><p>Goroutines</p></li><li><p>Channels</p></li><li><p>Race conditions are bad</p></li><li><p>The <code>select</code> keyword</p></li><li><p>Timing out a goroutine</p></li><li><p>Go channels revisited</p></li><li><p>Handling UNIX signals</p></li><li><p>Shared memory and shared variables</p></li><li><p>Closured variables and the <code>go</code> statement</p></li><li><p>The <code>context</code> package</p></li><li><p>The <code>semaphore</code> package</p></li><li><p>Making the statistics application concurrent</p></li></ul><div><hr></div><h1>Processes, threads, and goroutines</h1><p>A process is an OS representation of a running program, while a program is a binary file on a disk that contains all the information necessary for creating an OS process. The binary file is written in a specific format and contains all the instructions the CPU is going to run, as well as a plethora of other required sections. That program is loaded into memory and the instructions are executed, creating a running process. So, a process carries with it additional resources such as memory, opened file descriptions, and user data, as well as other types of resources that are obtained during runtime.</p><p>A thread is a smaller and lighter entity than a process. Processes consist of one or more threads that have their own flow of control and stack. A quick and simplistic way to differentiate a thread from a process is to consider a process as the running binary file and a thread as a subset of a process.</p><p>A goroutine is the minimum Go entity that can be executed concurrently. The use of the word <em>minimum</em> is very important here, as goroutines are not autonomous entities like UNIX processes&#8212;<em><strong>goroutines live in OS threads that live in OS processes</strong></em>. The good thing is that goroutines are lighter than threads, which, in turn, are lighter than processes&#8212;running thousands or hundreds of thousands of goroutines on a single machine is not a problem. Among the reasons that goroutines are lighter than threads are that they have a smaller stack that can grow, they have a faster startup time, and they can communicate with each other through channels with low latency. In practice, this means that a process can have multiple threads and lots of goroutines, whereas a goroutine needs the environment of a process to exist. So, to create a goroutine, you need to have a process with at least one thread. The OS takes care of the process and thread scheduling, while Go creates the necessary threads and the developer creates the desired number of goroutines.</p><p>Now that you know the basics of processes, programs, threads, and goroutines, let us talk a little bit about the Go scheduler.</p><div><hr></div><h1>The Go scheduler</h1><p>The OS kernel scheduler is responsible for the execution of the threads of a program. Similarly, the Go runtime has its own scheduler, which is responsible for the execution of the goroutines using a technique known as <em>m:n scheduling</em>, where m goroutines are executed using n OS threads using multiplexing. The Go scheduler is the Go component responsible for the way and the order in which the goroutines of a Go program get executed. This makes the Go scheduler a really important part of the Go programming language. The Go scheduler is also executed as a goroutine.</p><p>Be aware that as the Go scheduler only deals with the goroutines of a single program, its operation is much simpler, cheaper, and faster than the operation of the OS kernel scheduler.</p><p>Go uses the fork-join concurrency model. The <em>fork part</em> of the model, which should not be confused with the <code>fork(2)</code> system call, states that a child branch can be created at any point of a program. Analogously, the <em>join part</em> of the Go concurrency model is where the child branch ends and joins with its parent. Keep in mind that both <code>sync.Wait()</code> statements and channels that collect the results of goroutines are join points, whereas each new goroutine creates a child branch.</p><p><em><strong>The fair scheduling strategy shares all load evenly among the available processors</strong></em>. At first, this might look like the perfect strategy because it does not have to take many things into consideration while keeping all processors equally occupied. However, it turns out that this is not exactly the case because most distributed tasks usually depend on other tasks. Therefore, some processors are underutilized or, equivalently, some processors are utilized more than others.</p><p>A goroutine is a task, whereas everything after the calling statement of a goroutine is a continuation. <em><strong>In the work-stealing strategy used by the Go scheduler, a (logical) processor that is underutilized looks for additional work from other processors.</strong></em> When it finds such jobs, it steals them from the other processor or processors, hence the name. Additionally, the work-stealing algorithm of Go queues and steals continuations. A stalling join, as is suggested by its name, is a point where a thread of execution stalls at a join and starts looking for other work to do.</p><p>Although both task stealing and continuation stealing have stalling joins, continuations happen more often than tasks; therefore, the Go scheduling algorithm works with continuations rather than tasks. The main disadvantage of continuation stealing is that it requires extra work from the compiler of the programming language. Fortunately, Go provides that extra help and, therefore, uses continuation stealing in its work-stealing algorithm. One of the benefits of continuation stealing is that you get the same results when using function calls instead of goroutines or a single thread with multiple goroutines. This makes perfect sense, as only one thing is executed at any given point in both cases.</p><p>The Go scheduler works using three main kinds of entities: OS threads (M), which are related to the OS in use, goroutines (G), and logical processors (P). The number of processors that can be used by a Go program is specified by the value of the <code>GOMAXPROCS</code> environment variable&#8212;at any given time, there are, at most, <code>GOMAXPROCS</code> processors. Now, let us return to the <code>m:n</code> scheduling algorithm used in Go. Strictly speaking, at any time, you have <code>m</code> goroutines that are executed and, therefore, scheduled to run, on <code>n</code> OS threads using, at most, <code>GOMAXPROCS</code> number of logical processors. You will learn more about <code>GOMAXPROCS</code> shortly.</p><p>Each goroutine can be in one of the following three stages: <em>executing</em>, <em>runnable</em>, or <em>waiting</em>. In the executing stage, the instructions of the goroutine are executed on an OS thread. In the runnable stage, the goroutine waits to be assigned to an OS thread for execution. Finally, in the waiting stage, the goroutine is blocked for some reason like waiting for a resource or a mutex to become available to go into one of the other two stages.</p><p>The following figure shows that there are two different kinds of queues&#8212;a global run queue and a local run queue&#8212;attached to each logical processor. Goroutines from the global queue are assigned to the queue of a logical processor in order to get executed at some point in the future.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cfJX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76c15a98-fdcc-4810-a793-b6e91542061d_472x316.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cfJX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76c15a98-fdcc-4810-a793-b6e91542061d_472x316.png 424w, https://substackcdn.com/image/fetch/$s_!cfJX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76c15a98-fdcc-4810-a793-b6e91542061d_472x316.png 848w, https://substackcdn.com/image/fetch/$s_!cfJX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76c15a98-fdcc-4810-a793-b6e91542061d_472x316.png 1272w, https://substackcdn.com/image/fetch/$s_!cfJX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76c15a98-fdcc-4810-a793-b6e91542061d_472x316.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cfJX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76c15a98-fdcc-4810-a793-b6e91542061d_472x316.png" width="472" height="316" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/76c15a98-fdcc-4810-a793-b6e91542061d_472x316.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:316,&quot;width&quot;:472,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!cfJX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76c15a98-fdcc-4810-a793-b6e91542061d_472x316.png 424w, https://substackcdn.com/image/fetch/$s_!cfJX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76c15a98-fdcc-4810-a793-b6e91542061d_472x316.png 848w, https://substackcdn.com/image/fetch/$s_!cfJX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76c15a98-fdcc-4810-a793-b6e91542061d_472x316.png 1272w, https://substackcdn.com/image/fetch/$s_!cfJX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76c15a98-fdcc-4810-a793-b6e91542061d_472x316.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.1: The operation of the Go scheduler</em></p><p>Each logical processor can have multiple threads, and the stealing occurs between the local queues of the available logical processors. Finally, keep in mind that the Go scheduler is allowed to create more OS threads when needed. OS threads are expensive in terms of resources and going from one status to another (<em>context switching</em>), which means that dealing too much with OS threads might slow down your Go applications.</p><p>Next, we discuss the meaning and the use of <code>GOMAXPROCS</code>.</p><h2>The GOMAXPROCS environment variable</h2><p>The <code>GOMAXPROCS</code> environment variable allows you to set the number of OS threads that can execute user-level Go code simultaneously; this does not limit the number of threads created but it does limit the number of threads that are actively running. Starting with Go version 1.5, the default value of <code>GOMAXPROCS</code> should be the number of logical cores available in your machine. There is also the <code>runtime.GOMAXPROCS()</code> function, which allows you to set and get the value of <code>GOMAXPROCS</code> programmatically.</p><p>If you decide to assign a value to <code>GOMAXPROCS</code> that is smaller than the number of cores in your machine, you might affect the performance of your program. However, using a <code>GOMAXPROCS</code> value that is larger than the number of available cores does not necessarily make your Go programs run faster due to the context switching of threads.</p><p>As mentioned earlier in this subsection, you can programmatically set and get the value of the <code>GOMAXPROCS</code> environment variable&#8212;this is illustrated in <code>maxprocs.go</code>, which will also show additional capabilities of the runtime package. The <code>main()</code> function is implemented as follows:</p><pre><code><code>func main() {
    fmt.Print("You are using ", runtime.Compiler, " ")
    fmt.Println("on a", runtime.GOARCH, "machine")
    fmt.Println("Using Go version", runtime.Version())</code></code></pre><p>The <code>runtime.Compiler</code> variable holds the compiler toolchain used for building the running binary. The two most well-known values are <code>gc</code> and <code>gccgo</code>. The <code>runtime.GOARCH</code> variable holds the current architecture and <code>runtime.Version()</code> returns the current version of the Go compiler. This information is not necessary for using <code>runtime.GOMAXPROCS()</code> but it is good to have a better knowledge of your system.</p><pre><code><code>    fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
}</code></code></pre><p>What happens with the <code>runtime.GOMAXPROCS(0)</code> call? It always returns the previous value of the maximum number of CPUs that can be executed simultaneously. When the parameter of <code>runtime.GOMAXPROCS()</code> is equal to or bigger than <code>1</code>, then <code>runtime.GOMAXPROCS()</code> also changes the current setting. As we are using <code>0</code>, our call does not alter the current setting.</p><p>Running <code>maxprocs.go</code> produces the following output:</p><pre><code><code>You are using gc on a arm64 machine
Using Go version go1.21.0
GOMAXPROCS: 10</code></code></pre><p>You can change the value of <code>GOMAXPROCS</code> on the fly using the following technique:</p><pre><code><code>$ GOMAXPROCS=100; go run maxprocs.go
You are using gc on a amd64 machine
Using Go version go1.21.0
GOMAXPROCS: 100</code></code></pre><p>The previous command temporarily changes the value of <code>GOMAXPROCS</code> to <code>100</code> and runs <code>maxprocs.go</code>.</p><p>Apart from testing the performance of your code using fewer cores, you will most likely not need to change <code>GOMAXPROCS</code>. The next subsection will explain the similarities and differences between concurrency and parallelism.</p><h2>Concurrency and parallelism</h2><p>It is a common misconception that concurrency is the same thing as parallelism&#8212;this is not true! Parallelism is the simultaneous execution of multiple entities of some kind, whereas concurrency is a way of structuring your components so that they can be executed independently when possible.</p><p>It is only when you build software components concurrently that you can safely execute them in parallel, when and if your OS and your hardware permit it. The Erlang programming language did this a long time ago&#8212;long before CPUs had multiple cores and computers had lots of RAM.</p><p>In a valid concurrent design, adding concurrent entities makes the whole system run faster because more things can be executed in parallel. So, the desired parallelism comes from a better concurrent expression and implementation of the problem. The developer is responsible for taking concurrency into account during the design phase of a system and will benefit from a potential parallel execution of the components of the system. So, the developer should not think about parallelism but about breaking things into independent components that solve the initial problem when combined.</p><p><em><strong>Even if you cannot run your functions in parallel on your machine, a valid concurrent design still improves the design, data flow, and maintainability of your programs</strong></em>. In other words, concurrency is better than parallelism! Let us now talk about goroutines before looking into channels, which are the main components of the Go concurrency model.</p><div><hr></div><h1>Goroutines</h1><p>You can define, create, and execute a new goroutine using the <code>go</code> keyword followed by a named function or an anonymous function call. The <code>go</code> keyword makes the function call return immediately, while the function starts running in the background as a goroutine and the rest of the program continues its execution. You cannot control or make any assumptions about the order in which your goroutines are going to be executed because that depends on the scheduler of the OS, the Go scheduler, and the load of the OS.</p><h2>Creating a goroutine</h2><p>In this subsection, you will learn how to create goroutines. The program that illustrates the technique is called <code>create.go</code>. The implementation of the <code>main()</code> function is as follows:</p><pre><code><code>func main() {
    go func(x int) {
        fmt.Printf("%d ", x)
    }(10)</code></code></pre><p>This is how you run an anonymous function as a goroutine. The <code>(10)</code> at the end is how you pass a parameter to an anonymous function. The previous anonymous function just prints a value. In general, it is more readable to pass parameters explicitly than to have the function close over the variables it uses.</p><pre><code><code>    go printme(15)</code></code></pre><p>This is how you execute a function as a goroutine. As a rule of thumb, the functions that you execute as goroutines should not return any values directly. <em><strong>Exchanging data with goroutines happens via the use of shared memory or channels or some other mechanism</strong></em>.</p><pre><code><code>    time.Sleep(time.Second)
    fmt.Println("Exiting...")
}</code></code></pre><p>As a Go program does not wait for its goroutines to end before exiting, we need to delay it manually, which is the purpose of the <code>time.Sleep()</code> call. We correct that shortly in order to wait for all goroutines to finish before exiting.</p><p>Running <code>create.go </code>produces the following output:</p><pre><code><code>$ go run create.go 
10 * 15
Exiting...</code></code></pre><p>The <code>10</code> part in the output is from the anonymous function, whereas the <code>* 15</code> part is from the <code>go printme(15)</code> statement. However, if you run <code>create.go</code> more than once, you might get a different output because the two goroutines are not always executed in the same order, as this depends on the Go scheduler:</p><pre><code><code>$ go run create.go
* 15
10 Exiting...</code></code></pre><p>The next subsection shows how to run a variable number of goroutines.</p><h2>Creating multiple goroutines</h2><p>In this subsection, you will learn how to create a variable number of goroutines. The program that illustrates the technique is called <code>multiple.go</code>. The number of goroutines is given as a command line argument to the program. The important code from the implementation of the <code>main()</code> function is the following:</p><pre><code><code>fmt.Printf("Going to create %d goroutines.\n", count)
for i := 0; i &lt; count; i++ {</code></code></pre><p>There is nothing prohibiting you from using a <code>for</code> loop to create multiple goroutines, especially when you want to create lots of them.</p><pre><code><code>    go func(x int) {
        fmt.Printf("%d ", x)
    }(i)
}
time.Sleep(time.Second)
fmt.Println("\nExiting...")</code></code></pre><p>Once again, <code>time.Sleep()</code> prevents the <code>main()</code> function from exiting immediately.</p><p>Running <code>multiple.go</code> generates the following kind of output:</p><pre><code><code>$ go run multiple.go 15
Going to create 15 goroutines.
3 0 8 4 5 6 7 11 9 12 14 13 1 2 10 
Exiting...</code></code></pre><p>If you run <code>multiple.go</code> many times, you are going to get different outputs. So, there is still room for improvement. The next subsection shows how to remove the call to <code>time.Sleep()</code> and make your programs wait for the goroutines to finish.</p><h2>Waiting for all goroutines to finish</h2><p>It is not enough to create multiple goroutines&#8212;you also need to wait for them to finish before the <code>main()</code> function ends. Therefore, this subsection shows a very popular technique that improves the code of <code>multiple.go</code>&#8212;the improved version is called <code>varGoroutines.go</code>. But first, we need to explain how this works.</p><p>The synchronization process begins by defining a <code>sync.WaitGroup</code> variable and using the <code>Add()</code>, <code>Done()</code>, and <code>Wait()</code> methods. If you look at the source code of the <code>sync</code> Go package, and more specifically at the <code>waitgroup.go</code> file, you see that the <code>sync.WaitGroup</code> type is nothing more than a structure with two fields:</p><pre><code><code>type WaitGroup struct {
    noCopy noCopy
    state1 [3]uint32
}</code></code></pre><p>Each call to <code>sync.Add()</code> increases a counter in the <code>state1</code> field, which is an array with three <code>uint32</code> elements. Notice that it is really important to call <code>sync.Add()</code> before the <code>go</code> statement in order to prevent any <em>race conditions</em>&#8212;we will learn about race conditions in the <em>Race conditions are bad</em> section. When each goroutine finishes its job, the <code>sync.Done()</code> function should be executed in order to decrease the same counter by one. Behind the scenes, <code>sync.Done()</code> runs an <code>Add(-1)</code> call. The <code>Wait()</code> method waits until that counter becomes <code>0</code> in order to return. The return of <code>Wait()</code> inside the <code>main()</code> function means that <code>main()</code> is going to return and the program ends.</p><p>You can call <code>Add()</code> with a positive integer value other than 1 in order to avoid calling <code>Add(1)</code> multiple times. This can be handy when you know the number of goroutines you are going to create in advance. <code>Done()</code> does not support that functionality.</p><p>The important part of <code>varGoroutines.go</code> is the following:</p><pre><code><code>var waitGroup sync.WaitGroup
fmt.Printf("%#v\n", waitGroup)</code></code></pre><p>This is where you create a <code>sync.WaitGroup</code> variable that you are going to use. The <code>fmt.Printf()</code> call prints the contents of the <code>sync.WaitGroup</code> structure&#8212;you do not usually do that but it is good for learning more about the <code>sync.WaitGroup</code> structure.</p><pre><code><code>for i := 0; i &lt; count; i++ {
    waitGroup.Add(1)</code></code></pre><p>We call <code>Add(1)</code> just before we create the goroutine in order to avoid race conditions.</p><pre><code><code>    go func(x int) {
        defer waitGroup.Done()</code></code></pre><p>The <code>Done()</code> call is going to be executed just before the anonymous function returns because of the <code>defer</code> keyword.</p><pre><code><code>        fmt.Printf("%d ", x)
    }(i)
}
fmt.Printf("%#v\n", waitGroup)
waitGroup.Wait()</code></code></pre><p>The <code>Wait()</code> function waits for the counter in the <code>waitGroup</code> variable to become <code>0</code> before it returns, which is what we want to achieve.</p><pre><code><code>fmt.Println("\nExiting...")</code></code></pre><p>When the <code>Wait()</code> function returns, the <code>fmt.Println()</code> statement is executed. No need to call <code>time.Sleep()</code> any more!</p><p>Running <code>varGoroutines.go</code> produces the following output:</p><pre><code><code>$ go run varGoroutines.go 15
Going to create 10 goroutines.
sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x0}, sema:0x0}
sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0xa00000000}, sema:0x0}
14 8 9 10 11 5 0 4 1 2 3 6 13 12 7 
Exiting...</code></code></pre><p>Remember that using more goroutines in a program is not a panacea for performance, as more goroutines, in addition to the various calls to <code>sync.Add()</code>, <code>sync.Wait()</code>, and <code>sync.Done()</code>, might slow down your program due to the extra housekeeping that needs to be done by the Go scheduler and the Go garbage collector.</p><h2>What if the number of Add() and Done() calls differ?</h2><p>When the number of <code>sync.Add()</code> calls and <code>sync.Done()</code> calls are equal, everything is going to be fine in your programs. However, this subsection tells you what happens when these two numbers do not agree with each other.</p><p>Without giving any command line parameters to <code>addDone.go</code>, the number of <code>Add()</code> calls is going to be smaller than the number of <code>Done()</code> calls. With at least one command line argument, the number of <code>Done()</code> calls is going to be smaller than the number of <code>Add()</code> calls. You can look at the Go code of <code>addDone.go</code> on your own. What is important is the output it generates. Running <code>addDone.go</code> without any command line arguments produces the following error message:</p><pre><code><code>$ go run addDone.go
Going to create 20 goroutines.
sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x0}, sema:0x0}
sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x1300000000}, sema:0x0}
19 3 4 5 6 7 8 9 10 11 12 13 14 15 16 2 1 17 18 0
Exiting...
panic: sync: negative WaitGroup counter
goroutine 20 [running]:
sync.(*WaitGroup).Add(0x1?, 0x1?)
    /opt/homebrew/Cellar/go/1.21.0/libexec/src/sync/waitgroup.go:62 +0x108
sync.(*WaitGroup).Done(0x0?)
    /opt/homebrew/Cellar/go/1.21.0/libexec/src/sync/waitgroup.go:87 +0x20
main.main.func1(0x0?)
    ~/go/src/github.com/mactsouk/mGo4th/ch08/addDone.go:26 +0x9c
created by main.main in goroutine 1
    ~/go/src/github.com/mactsouk/mGo4th/ch08/addDone.go:23 +0xec
exit status 2</code></code></pre><p>The cause of the error message can be found in the output: <code>panic: sync: negative WaitGroup counter</code>.</p><p>Sometimes, <code>addDone.go</code> does not produce any error messages and terminates just fine&#8212;<em><strong>this mainly happens when the system is already busy</strong></em>. This is an issue with concurrent programs in general&#8212;they do not always crash or misbehave as the order of execution can change, which might change the behavior of the program. This makes the debugging of concurrent software even more difficult.</p><p>Running <code>addDone.go</code> with one command line argument produces the following error message:</p><pre><code><code>$ go run addDone.go 1
Going to create 20 goroutines.
sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x0}, sema:0x0}
sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x1500000000}, sema:0x0}
19 1 2 11 12 13 14 15 16 17 18 6 3 4 5 8 7 9 0 10 fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x0?)
    /opt/homebrew/Cellar/go/1.21.0/libexec/src/runtime/sema.go:62 +0x2c
sync.(*WaitGroup).Wait(0x14000128030)
    /opt/homebrew/Cellar/go/1.21.0/libexec/src/sync/waitgroup.go:116 +0x78
main.main()
    ~/go/src/github.com/mactsouk/mGo4th/code/ch08/addDone.go:38 +0x230
exit status 2
</code></code></pre><p>Once again, the reason for the crash is printed on the screen: <code>fatal error: all goroutines are asleep - deadlock!</code>. This means that the program should wait indefinitely for a goroutine to finish&#8212;that is, for a <code>Done()</code> call that is never going to happen.</p><h2>Creating multiple files with goroutines</h2><p>As a practical example of the use of goroutines, this subsection presents a command line utility that creates multiple files populated with randomly generated data&#8212;such files can be used for testing file systems or generating data for testing. The crucial code of <code>randomFiles.go</code> is the following:</p><pre><code><code>var waitGroup sync.WaitGroup
for i := start; i &lt;= end; i++ {
    waitGroup.Add(1)
    
    go func(n int) {
        filepath := filepath.Join(path, fmt.Sprintf("%s%d", filename, n))
        defer waitGroup.Done()
        createFile(filepath)
    }(i)
}
waitGroup.Wait()
</code></code></pre><p>We first create a <code>sync.WaitGroup</code> variable in order to wait for all goroutines to finish in the right way. Each file is created by a single goroutine only. What is important here is that each file has a unique filename&#8212;this is implemented with the <code>filepath</code> variable that contains the value of the <code>for</code> loop counter. Multiple <code>createFile()</code> functions executed as goroutines create the files. This is a simple yet very efficient way of creating multiple files. Running <code>randomFiles.go</code> generates the following output:</p><pre><code><code>$ go run randomFiles.go 
Usage: randomFiles firstInt lastInt filename directory</code></code></pre><p>So, the utility requires four parameters, which are the first and last values of the <code>for</code> loop as well as the filename and the directory in which the files are going to be written. So, let us run the utility with the correct number of parameters:</p><pre><code><code>$ go run randomFiles.go 3 5 masterGo /tmp
/tmp/masterGo3 created!
/tmp/masterGo5 created!
/tmp/masterGo4 created!</code></code></pre><p>Everything looks fine, and four files have been created according to our instructions! Now that we know about goroutines, let us continue with channels.</p><div><hr></div><h1>Channels</h1><p>A channel is a communication mechanism that, among other things, allows goroutines to exchange data. Firstly, each channel allows the exchange of a particular data type, which is also called the element type of the channel, and secondly, for a channel to operate properly, you need someone to receive what is sent via the channel. You should declare a new channel using <code>make()</code> and the <code>chan</code> keyword (<code>make(chan int)</code>), and you can close a channel using the <code>close()</code> function. You can declare the size of a channel by writing something similar to <code>make(chan int, 1)</code>. This statement creates a <em><strong>buffered channel</strong></em> that has a different use&#8212;buffered channels are explained later in this chapter.</p><p>Just because we can use channels, it does not mean that we should. If a simpler solution exists that allows goroutines to get executed and save the generated information, we should also consider that. The purpose of every developer should be to create a simple design, not to use all the features of a programming language.</p><p>A pipeline is a virtual method for connecting goroutines and channels so that the output of one goroutine becomes the input of another goroutine using channels to transfer your data. One of the benefits that you get from using pipelines is that there is a constant data flow in your program, as no goroutine or channel has to wait for everything to be completed in order to start their execution. Additionally, you use fewer variables and, therefore, less memory space because you do not have to save everything as a variable. Finally, the use of pipelines simplifies the design of the program and improves its maintainability.</p><h2>Writing to and reading from a channel</h2><p>Writing a value (<code>val</code>) to a channel (<code>ch</code>) is as easy as writing <code>ch &lt;- val</code>. The arrow shows the direction of the value, and you will have no problem with this statement as long as both <code>var</code> and <code>ch</code> are of the same data type.</p><p>You can read a single value from a channel named <code>c</code> by executing <code>&lt;-c</code>. In this case, the direction is from the channel to the outer world. You can save that value into a new variable using <code>aVar := &lt;-c</code>.</p><p>Both channel reading and writing are illustrated in <code>channels.go</code>, which comes with the following code:</p><pre><code><code>package main
import (
    "fmt"
    "sync"
)
func writeToChannel(c chan int, x int) {
    c &lt;- x
    close(c)
}</code></code></pre><p>This function just writes a value to the channel and immediately closes it.</p><pre><code><code>func printer(ch chan bool) {
    ch &lt;- true
}</code></code></pre><p>This function just sends the <code>true</code> value to a <code>bool</code> channel.</p><pre><code><code>func main() {
    c := make(chan int, 1)</code></code></pre><p>This channel is buffered with a size of 1. This means that as soon as we fill that buffer, we can close the channel and the goroutine is going to continue its execution and return. A channel that is unbuffered has a different behavior: when you try to send a value to that channel, it blocks forever because it is waiting for someone to fetch that value. In this case, we definitely want a buffered channel in order to avoid any blocking.</p><pre><code><code>    var waitGroup sync.WaitGroup
    waitGroup.Add(1)
    go func(c chan int) {
        defer waitGroup.Done()
        writeToChannel(c, 10)
        fmt.Println("Exit.")
    }(c)
    fmt.Println("Read:", &lt;-c)</code></code></pre><p>Here, we read from the channel and print the value without storing it in a separate variable.</p><pre><code><code>    _, ok := &lt;-c
    if ok {
        fmt.Println("Channel is open!")
    } else {
        fmt.Println("Channel is closed!")
    }</code></code></pre><p>The previous code shows a technique for determining whether a channel is closed or not. In this case, we are ignoring the read value&#8212;if the channel was open, then the read value would be discarded.</p><pre><code><code>    waitGroup.Wait()
    var ch chan bool = make(chan bool)
    for i := 0; i &lt; 5; i++ {
        go printer(ch)
    }</code></code></pre><p>Here, we make an unbuffered channel and create five goroutines without any synchronization as we do not use any <code>Add()</code> calls.</p><pre><code><code>    // Range on channels
    // IMPORTANT: As the channel ch is not closed,
    // the range loop does not exit on its own.
    n := 0
    for i := range ch {</code></code></pre><p>The <code>range</code> keyword works with channels! However, a <code>range</code> loop on a channel only exits when the channel is closed or uses the <code>break</code> keyword.</p><pre><code><code>        fmt.Println(i)
        if i == true {
            n++
        }
        if n &gt; 2 {
            fmt.Println("n:", n)
            close(ch)
            break
        }
    }</code></code></pre><p>We close the <code>ch</code> channel when a condition is met and exit the <code>for</code> loop using <code>break</code>. Note that it is never a good idea to close a channel on the receiving end&#8212;this is presented here for the sake of the example. You are going to see the consequences of this decision in a while.</p><pre><code><code>    for i := 0; i &lt; 5; i++ {
        fmt.Println(&lt;-ch)
    }
}</code></code></pre><p>When trying to read from a closed channel, we get the zero value of its data type, so this <code>for</code> loop works just fine and does not cause any issues.</p><p>Running <code>channels.go</code> generates the following output:</p><pre><code><code>Exit.
Read: 10</code></code></pre><p>After writing the value <code>10</code> to the channel using <code>writeToChannel(c, 10)</code>, we read that value back.</p><pre><code><code>Channel is closed!
true
true
true</code></code></pre><p>The <code>for</code> loop with the <code>range</code> exits after three iterations&#8212;each iteration prints <code>true</code> on screen.</p><pre><code><code>n: 3
false
false
false
false
false</code></code></pre><p>These five <code>false</code> values are printed by the last <code>for</code> loop of the program.</p><p>Although everything looks fine with <code>channels.go</code>, there is a logical issue with it, which we will explain and resolve in the <em>Race conditions are bad</em> section. Additionally, if we run <code>channels.go</code> multiple times, it might crash. However, most of the time, it does not, which makes debugging even more challenging.</p><h2>Receiving from a closed channel</h2><p><em><strong>Reading from a closed channel returns the zero value of its data type</strong></em>. However, if you try to write to a closed channel, your program is going to crash in a bad way (panic). These two situations are explored in <code>readCloseCh.go</code> and, more specifically, in the implementation of the <code>main()</code> function:</p><pre><code><code>func main() {
    willClose := make(chan complex64, 10)</code></code></pre><p>If you make that an unbuffered channel, the program is going to crash.</p><pre><code><code>    // Write some data to the channel
    willClose &lt;- -1
    willClose &lt;- 1i</code></code></pre><p>We write two values to the <code>willClose</code> channel.</p><pre><code><code>    // Read data and empty channel
    &lt;-willClose
    &lt;-willClose
    close(willClose)</code></code></pre><p>Then, we read and discard these two values and we close the channel.</p><pre><code><code>    // Read again - this is a closed channel
    read := &lt;-willClose
    fmt.Println(read)
}</code></code></pre><p>The last value that we read from the channel is the zero value of a <code>complex64</code> data type. Running <code>readCloseCh.go</code> generates the following output:</p><pre><code><code>(0+0i)</code></code></pre><p>So, we got back the zero value of the <code>complex64</code> data type. Now let us continue and discuss how to work with functions that accept channels as parameters.</p><h2>Channels as function parameters</h2><p>When using a channel as a function parameter, you can specify its direction&#8212;that is, whether it is going to be used for sending or receiving data only. In my opinion, if you know the purpose of a channel in advance, you should use this capability because it makes your programs more robust. You will not be able to send data accidentally to a channel from which you should only receive data, or receive data from a channel to which you should only be sending data.</p><p>If you declare that a channel function parameter is going to be used for reading only and you try to write to it, you are going to get a compile error message that will most likely save you from nasty bugs in the future. This is the major benefit of this approach!</p><p>All these are illustrated in <code>channelFunc.go</code>&#8212;the implementation of the functions that accept channel parameters are the following:</p><pre><code><code>func printer(ch chan&lt;- bool) {
    ch &lt;- true
}</code></code></pre><p>The above function accepts a channel parameter that is available for writing only.</p><pre><code><code>func writeToChannel(c chan&lt;- int, x int) {
    fmt.Println("1", x)
    c &lt;- x
    fmt.Println("2", x)
}</code></code></pre><p>The channel parameter of the above function is available for reading only.</p><pre><code><code>func f2(out &lt;-chan int, in chan&lt;- int) {
    x := &lt;-out
    fmt.Println("Read (f2):", x)
    in &lt;- x
    return
}</code></code></pre><p>The last function accepts two channel parameters. However, <code>out</code> is available for reading, whereas <code>in</code> is offered for writing. If you try to perform an operation on a channel parameter that is not allowed, the Go compiler is going to complain. This happens even if the function is not being used.</p><p>The subject of the next section is race conditions&#8212;read it carefully in order to avoid undefined behaviors and unpleasant situations when working with multiple goroutines.</p><h1>Race conditions are bad</h1><p>A <em>data race condition</em> is a situation where two or more running elements, such as threads and goroutines, try to take control of or modify a shared resource or shared variable of a program. Strictly speaking, a data race occurs when two or more instructions access the same memory address, where at least one of them performs a write (change) operation. If all operations are read operations, then there is no race condition. In practice, this means that you might get different output if you run your program multiple times, and that is a bad thing.</p><p>Using the <code>-race</code> flag when running or building Go source files executes the Go race detector, which makes the compiler create a modified version of a typical executable file. This modified version can record all accesses to shared variables as well as all synchronization events that take place, including calls to <code>sync.Mutex</code> and <code>sync.WaitGroup</code>, which are presented later on in this chapter. After analyzing the relevant events, the race detector prints a report that can help you identify potential problems so that you can correct them.</p><h2>The Go race detector</h2><p>You can run the race detector tool with <code>go run -race</code>. If we test <code>channels.go</code> using <code>go run -race</code>, we are going to get the following output:</p><pre><code><code>$ go run -race channels.go 
Exit.
Read: 10
Channel is closed!
true
true
true
n: 3
==================
WARNING: DATA RACE
Write at 0x00c000094010 by main goroutine:
  runtime.recvDirect()
      /opt/homebrew/Cellar/go/1.21.0/libexec/src/runtime/chan.go:348 +0x7c
  main.main()
      ~/go/src/github.com/mactsouk/mGo4th/ch08/channels.go:54 +0x444
Previous read at 0x00c000094010 by goroutine 10:
  runtime.chansend1()
      /opt/homebrew/Cellar/go/1.21.0/libexec/src/runtime/chan.go:146 +0x2c
  main.printer()
      ~/go/src/github.com/mactsouk/mGo4th/ch08/channels.go:14 +0x34
  main.main.func3()
      ~/go/src/github.com/mactsouk/mGo4th/ch08/channels.go:40 +0x34
Goroutine 10 (running) created at:
  main.main()
      ~/go/src/github.com/mactsouk/mGo4th/ch08/channels.go:40 +0x2b8
==================
false
false
false
false
false
panic: send on closed channel
goroutine 36 [running]:
main.printer(0x0?)
    ~/go/src/github.com/mactsouk/mGo4th/ch08/channels.go:14 +0x38
created by main.main in goroutine 1
    ~/go/src/github.com/mactsouk/mGo4th/ch08/channels.go:40 +0x2bc
exit status 2</code></code></pre><p>Therefore, although <code>channels.go</code> looks fine at first, there is a race condition waiting to happen. Let us now discuss where the problem with <code>channels.go</code> lies based on the previous output. There is a closing of a channel at <code>channels.go</code> on line 54, and there is a write to the same channel on line 14 that looks to be the root of the race condition situation.</p><p>Line 54 is <code>close(ch)</code>, whereas line 14 is <code>ch &lt;- true</code>. The issue is that we cannot be sure about what is going to happen and in which order&#8212;this is the race condition. If you execute <code>channels.go</code> without the race detector, it might work, but if you try it multiple times, you might get a <code>panic: send on closed channel</code> error message&#8212;this mainly has to do with the order in which the Go scheduler is going to run the goroutines of the program. So, if the closing of the channel happens first, then writing to that channel is going to fail&#8212;race condition!</p><p>Fixing <code>channels.go</code> requires changing the code and, more specifically, the implementation of the <code>printer()</code> function. The corrected version of <code>channels.go</code> is named <code>chRace.go</code> and comes with the following code:</p><pre><code><code>func printer(ch chan&lt;- bool, times int) {
    for i := 0; i &lt; times; i++ {
        ch &lt;- true
    }
    close(ch)
}
func main() {
    // This is an unbuffered channel
    var ch chan bool = make(chan bool)
    // Write 5 values to channel with a single goroutine
    go printer(ch, 5)
    // IMPORTANT: As the channel ch is closed,
    // the range loop is going to exit on its own.
    for val := range ch {
        fmt.Print(val, " ")
    }
    fmt.Println()
    for i := 0; i &lt; 15; i++ {
        fmt.Print(&lt;-ch, " ")
    }
    fmt.Println()
}</code></code></pre><p>The first thing to notice is that instead of using multiple goroutines for writing to the desired channel, we use a single goroutine. <em><strong>A single goroutine writing to a channel followed by the closing of that channel cannot create any race conditions because things happen sequentially</strong></em>.</p><p>Running <code>go run -race chRace.go</code> produces the following output, which means that there is no longer a race condition:</p><pre><code><code>true true true true true 
false false false false false false false false false false false false false false false</code></code></pre><p>The next section is about the important and powerful <code>select</code> keyword.</p><div><hr></div><h1>The select keyword</h1><p>The <code>select</code> keyword is really important because it allows you to <em><strong>listen to multiple channels at the same time</strong></em>. A <code>select</code> block can have multiple cases and an optional <code>default</code> case, which mimics the <code>switch</code> statement. It is good for <code>select</code> blocks to have a timeout option just in case. Lastly, a <code>select</code> without any cases (<code>select{}</code>) waits forever.</p><p>In practice, this means that <code>select</code> allows a goroutine to wait on multiple communication operations. So, <code>select</code> gives you the power to listen to multiple channels using a single select block. Consequently, you can have non-blocking operations on channels, provided that you have implemented your <code>select</code> blocks appropriately.</p><p>A <code>select</code> statement is <em><strong>not evaluated sequentially</strong></em>, as all of its channels are examined simultaneously. If none of the channels in a <code>select</code> statement are ready, the <code>select</code> statement blocks (waits) until one of the channels is ready. If multiple channels of a <code>select</code> statement are ready, then the Go runtime <em><strong>makes a random selection</strong></em> from the set of these ready channels.</p><p>The code in <code>select.go</code> presents a simple use of <code>select</code> running in a goroutine that has three cases. But first, let us see how the goroutine that contains <code>select</code> is executed:</p><pre><code><code>    wg.Add(1)
    go func() {
        gen(0, 2*n, createNumber, end)
        wg.Done()
    }()</code></code></pre><p>The previous code tells us that for <code>wg.Done()</code> to get executed, <code>gen()</code> should return first. So, let us see the implementation of <code>gen()</code>:</p><pre><code><code>func gen(min, max int, createNumber chan int, end chan bool) {
    time.Sleep(time.Second)
    for {
        select {
        case createNumber &lt;- rand.Intn(max-min) + min:
        case &lt;-end:
            fmt.Println("Ended!")
            // return</code></code></pre><p>The right thing to do here is to add the <code>return</code> statement for <code>gen()</code> to finish. Let us imagine that you have forgotten to add the <code>return</code> statement. This means that the function is not going to finish after the <code>select</code> branch associated with the end channel parameter is executed&#8212;<code>createNumber</code> is not going to end the function as it has no <code>return</code> statement. Therefore, the <code>select</code> block keeps waiting for more. The solution can be found in the code that follows:</p><pre><code><code>        case &lt;-time.After(4 * time.Second):
            fmt.Println("time.After()!")
            return
        }
    }
}</code></code></pre><p>So, what is really happening in the code of the entire select block? This particular <code>select</code> statement has three cases. As stated earlier, <code>select</code> does not require a <code>default</code> branch. You can consider the third branch of the <code>select</code> statement as a clever <code>default</code> branch. This happens because <code>time.After()</code> waits for the specified duration (<code>4 * time.Second</code>) to elapse and then prints a message and properly ends <code>gen()</code> with <code>return</code>. This unblocks the <code>select</code> statement in case all of the other channels are blocked for some reason. Although omitting <code>return</code> from the second branch is a bug, this shows that having an exit strategy is always a good thing.</p><p>Running <code>select.go</code> produces the following output:</p><pre><code><code>$ go run select.go 10
Going to create 10 random numbers.
13 0 2 8 12 4 13 15 14 19 Ended!
time.After()!
Exiting...</code></code></pre><p>We are going to see <code>select</code> in action in the remainder of the chapter, starting from the next section, which discusses how to time out goroutines. What you should remember is that <code>select</code> allows us to <em><strong>listen to multiple channels from a single point</strong></em>.</p><div><hr></div><h1>Timing out a goroutine</h1><p>There are times when goroutines take more time than expected to finish&#8212;in such situations, we want to time out the goroutines so that we can unblock the program. This section presents two such techniques.</p><h2>Timing out a goroutine inside main()</h2><p>This subsection presents a simple technique for timing out a goroutine. The relevant code can be found in the <code>main()</code> function of <code>timeOut1.go</code>:</p><pre><code><code>func main() {
    c1 := make(chan string)
    go func() {
        time.Sleep(3 * time.Second)
        c1 &lt;- "c1 OK"
    }()</code></code></pre><p>The <code>time.Sleep()</code> call is used for emulating the time it normally takes for a function to finish its operation. In this case, the anonymous function that is executed as a goroutine takes about three seconds before writing a message to the <code>c1</code> channel.</p><pre><code><code>    select {
    case res := &lt;-c1:
        fmt.Println(res)
    case &lt;-time.After(time.Second):
        fmt.Println("timeout c1")
    }</code></code></pre><p>The purpose of the <code>time.After()</code> call is to wait for the desired time before being executed&#8212;if another branch is executed, the waiting time resets. In this case, we are not interested in the actual value returned by <code>time.After()</code> but in the fact that the <code>time.After()</code> branch was executed, which means that the waiting time has passed. In this case, as the value passed to the <code>time.After()</code> function is smaller than the value used in the <code>time.Sleep()</code> call that was executed previously, you will most likely get a timeout message. The reason for saying &#8220;most likely&#8221; is that Linux is not a real-time OS and, sometimes, the OS scheduler plays strange games, especially when it has to deal with a high load and has to schedule lots of tasks&#8212;this means that you should not make any assumptions about the operation of the OS scheduler.</p><pre><code><code>    c2 := make(chan string)
    go func() {
        time.Sleep(3 * time.Second)
        c2 &lt;- "c2 OK"
    }()
    select {
    case res := &lt;-c2:
        fmt.Println(res)
    case &lt;-time.After(4 * time.Second):
        fmt.Println("timeout c2")
    }
}</code></code></pre><p>The preceding code executes a goroutine that takes about three seconds to execute because of the <code>time.Sleep()</code> call and defines a timeout period of four seconds in select using <code>time.After(4 * time.Second)</code>. If the <code>time.After(4 * time.Second)</code> call returns after you get a value from the <code>c2</code> channel found in the first case of the <code>select</code> block, then there will be no timeout; otherwise, you will get a timeout. However, in this case, the value of the <code>time.After()</code> call provides enough time for the <code>time.Sleep()</code> call to return, so you will most likely not get a timeout message here.</p><p>Let us now verify our thoughts. Running <code>timeOut1.go</code> produces the following output:</p><pre><code><code>$ go run timeOut1.go 
timeout c1
c2 OK</code></code></pre><p>As expected, the first goroutine timed out, whereas the second one did not. The subsection that follows presents another timeout technique.</p><h2>Timing out a goroutine outside main()</h2><p>This subsection illustrates another technique for timing out goroutines. The <code>select</code> statement can be found in a separate function. Additionally, the timeout period is given as a command line argument.</p><p>The interesting part of <code>timeOut2.go</code> is the implementation of <code>timeout()</code>:</p><pre><code><code>func timeout(t time.Duration) {
    temp := make(chan int)
    go func() {
        time.Sleep(5 * time.Second)
        defer close(temp)
    }()
    select {
    case &lt;-temp:
        result &lt;- false
    case &lt;-time.After(t):
        result &lt;- true
    }
}</code></code></pre><p>In <code>timeout()</code>, the time duration that is used in the <code>time.After()</code> call is a function parameter, which means that it can vary. Once again, the <code>select</code> block supports the logic of the timeout. Any timeout period longer than 5 seconds will most likely give the goroutine enough time to finish. If <code>timeout()</code> writes <code>false</code> to the <code>result</code> channel, then there is no timeout, whereas if it writes <code>true</code>, there is a timeout. Running <code>timeOut2.go</code> produces the following output:</p><pre><code><code>$ go run timeOut2.go 100
Timeout period is 100ms
Time out!</code></code></pre><p>The timeout period is 100 milliseconds, which means that the goroutine did not have enough time to finish, hence the timeout message.</p><pre><code><code>$ go run timeOut2.go 5500 
Timeout period is 5.5s
OK</code></code></pre><p>This time, the timeout is 5,500 milliseconds, which means that the goroutine had enough time to finish.</p><p>The next section revisits and presents advanced concepts related to channels.</p><div><hr></div><h1>Go channels revisited</h1><p>So far, we have seen the basic usages of channels&#8212;this section presents the definition and the usage of <code>nil</code> channels, signal channels, and buffered channels.</p><p>Although channels seem like an interesting concept, they are not the answer to every concurrency problem as there exist times when they can be replaced by mutexes and shared memory. So, <em><strong>do not force the use of channels</strong></em>.</p><p>It helps to remember that the zero value of the channel type is <code>nil</code>, and if you send a message to a closed channel, the program panics. However, if you try to read from a closed channel, you get the zero value of the type of that channel. So, after closing a channel, you can no longer write to it but you can still read from it. To be able to close a channel, the channel must not be receive-only.</p><p>Additionally, a <code>nil</code> channel always blocks, which means that both reading and writing from <code>nil</code> channels block. This property of channels can be very useful when you want to disable a branch of a <code>select</code> statement by assigning the <code>nil</code> value to a channel variable. Finally, if you try to close a <code>nil</code> channel, your program is going to panic. This is best illustrated in the <code>closeNil.go</code> program:</p><pre><code><code>package main
func main() {
    var c chan string</code></code></pre><p>The previous statement defines a <code>nil</code> channel named <code>c</code> of the type <code>string</code>.</p><pre><code><code>    close(c)
}</code></code></pre><p>Running <code>closeNil.go</code> generates the following output:</p><pre><code><code>panic: close of nil channel
goroutine 1 [running]:
main.main()
    ~/go/src/github.com/mactsouk/mGo4th/ch08/closeNil.go:5 +0x20
exit status 2</code></code></pre><p>The previous output shows the message you are going to get if you try to close a <code>nil</code> channel. Let us now discuss buffered channels.</p><h2>Buffered channels</h2><p>Unlike unbuffered channels, which have a capacity of 0 and require a sender to have a corresponding receiver ready at the other end, buffered channels allow a certain number of values to be sent into the channel before a receiver is needed.</p><p>These channels allow us to put jobs in a queue quickly to be able to deal with more requests and process requests later on. Moreover, you can use buffered channels as semaphores to limit the throughput of your application.</p><p>The presented technique works as follows: all incoming requests are forwarded to a channel, which processes them one by one. When the channel is done processing a request, it sends a message to the original caller saying that it is ready to process a new one. So, <em><strong>the capacity of the buffer of the channel restricts the number of simultaneous requests that it can keep</strong></em>. Bear in mind that it is not the channel that processes the requests or sends the messages.</p><p>Also, bear in mind that a buffered channel keeps accepting data until it blocks due to its limited capacity. However, in the presented example, the implementation is what cancels remaining requests after the channel buffer is full due to the <code>select</code> statement, not the channel itself. The source file that implements the technique is named <code>bufChannel.go</code> and contains the following code:</p><pre><code><code>package main
import (
    "fmt"
)
func main() {
    numbers := make(chan int, 5)</code></code></pre><p>The <code>numbers</code> channel can store up to five integers because it is a buffer channel with a capacity of 5.</p><pre><code><code>    counter := 10
    for i := 0; i &lt; counter; i++ {
        select {
        // This is where the processing takes place
        case numbers &lt;- i * i:
            fmt.Println("About to process", i)
        default:
            fmt.Print("No space for ", i, " ")
        }</code></code></pre><p>We begin putting data into <code>numbers</code>&#8212;however, when the channel is full, it is not going to store more data and the <code>default</code> branch is going to be executed. This is not because of the way channels work but because of the specific implementation with <code>select</code>.</p><pre><code><code>    }
    fmt.Println()
    for {
        select {
        case num := &lt;-numbers:
            fmt.Print("*", num, " ")
        default:
            fmt.Println("Nothing left to read!")
            return
        }
    }
}</code></code></pre><p>Similarly, we try to read data from numbers using a <code>for</code> loop. When all data from the channel is read, the <code>default</code> branch is going to be executed and will terminate the program with its <code>return</code> statement&#8212;when <code>main()</code> returns, the entire program will be terminated.</p><p>Running <code>bufChannel.go</code> produces the following output:</p><pre><code><code>$ go run bufChannel.go 
About to process 0
. . .
About to process 4
No space for 5 No space for 6 No space for 7 No space for 8 No space for 9 
*0 *1 *4 *9 *16 Nothing left to read!
</code></code></pre><p>Let us now discuss <code>nil</code> channels.</p><h2>nil channels</h2><p><code>nil</code> channels <em><strong>always block</strong></em>! Therefore, you should use them when you want that behavior on purpose! The code that follows illustrates <code>nil</code> channels:</p><pre><code><code>package main
import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)
var wg sync.WaitGroup</code></code></pre><p>We are making <code>wg</code> a global variable in order to be available from anywhere in the code and avoid passing it to every function that needs it as a parameter. This is not idiomatic Go and some people might dislike that approach, despite its simpler implementation. An alternative would be to declare <code>wg</code> in <code>main()</code> and pass a pointer to each function that needs it&#8212;you can implement that as an exercise.</p><pre><code><code>func add(c chan int) {
    sum := 0
    t := time.NewTimer(time.Second)
    for {
        select {
        case input := &lt;-c:
            sum = sum + input
        case &lt;-t.C:
            c = nil
            fmt.Println(sum)
            wg.Done()
        }
    }
}</code></code></pre><p>The <code>send()</code> function keeps sending random numbers to channel <code>c</code>. Do not confuse channel <code>c</code>, which is a (channel) function parameter, with channel <code>t.C</code>, which is part of timer <code>t</code>&#8212;you can change the name of the <code>c</code> variable but not the name of the <code>C</code> field of a timer. When the time of timer <code>t</code> expires, the timer sends a value to the <code>t.C</code> channel.</p><p>This triggers the execution of the relevant branch of the <code>select</code> statement, which assigns the value <code>nil</code> to channel <code>c</code> and prints the value of the <code>sum</code> variable, and <code>wg.Done()</code> is executed, which is going to unblock <code>wg.Wait()</code> found in the <code>main()</code> function. Additionally, as <code>c</code> becomes <code>nil</code>, it stops/blocks <code>send()</code> from sending more data to it.</p><pre><code><code>func send(c chan int) {
    for {
        c &lt;- rand.Intn(10)
    }
}
func main() {
    c := make(chan int)
    rand.Seed(time.Now().Unix())
    wg.Add(1)
    go add(c)
    go send(c)
    wg.Wait()
}</code></code></pre><p>Running <code>nilChannel.go</code> produces the following output:</p><pre><code><code>$ go run nilChannel.go 
11168960</code></code></pre><p>Since the number of times that the first branch of the <code>select</code> statement in <code>add()</code> is going to be executed is not fixed, you get different results each time you execute <code>nilChannel.go</code>.</p><p>The next subsection discusses worker pools.</p><h2>Worker pools</h2><p>A worker pool is a <em><strong>set of threads that process jobs assigned to them</strong></em>. The Apache web server and the <code>net/http</code> package of Go more or less work this way: the main process accepts all incoming requests, which are forwarded to worker processes to get served. Once a worker process has finished its job, it is ready to serve a new client.</p><p>As Go does not have threads, the presented implementation is going to use goroutines instead of threads. Additionally, threads do not usually die after serving a request because the cost of ending a thread and creating a new one is too high, whereas goroutines do die after finishing their job. Worker pools in Go are implemented with the help of buffered channels, as they allow you to limit the number of goroutines running at the same time.</p><p>The presented utility implements a simple task: it processes integers and prints their square values using a single goroutine for serving each request. The code of <code>wPools.go</code> is as follows:</p><pre><code><code>package main
import (
    "fmt"
    "os"
    "runtime"
    "strconv"
    "sync"
    "time"
)
type Client struct {
    id      int
    integer int
}</code></code></pre><p>The <code>Client</code> structure is used for keeping track of the requests that the program is going to process.</p><pre><code><code>type Result struct {
    job    Client
    square int
}</code></code></pre><p>The <code>Result</code> structure is used for keeping the data of each <code>Client</code> as well as the results generated by the client. Put simply, the <code>Client</code> structure holds the input data of each request, whereas <code>Result</code> holds the results of a request&#8212;if you want to process more complex data, you should modify these structures.</p><pre><code><code>var size = runtime.GOMAXPROCS(0)
var clients = make(chan Client, size)
var data = make(chan Result, size)</code></code></pre><p>The <code>clients</code> and <code>data</code> buffered channels are used to get new client requests and write the results, respectively. If you want your program to run faster, you can increase the value of <code>size</code>.</p><pre><code><code>func worker(wg *sync.WaitGroup) {
    for c := range clients {
        square := c.integer * c.integer
        output := Result{c, square}
        data &lt;- output
        time.Sleep(time.Second)
    }
    wg.Done()
}</code></code></pre><p>The <code>worker()</code> function processes requests by reading the <code>clients</code> channel. Once the processing is complete, the result is written to the <code>data</code> channel. The delay that is introduced with <code>time.Sleep()</code> is not necessary but it gives you a better sense of the way that the generated output is printed.</p><pre><code><code>func create(n int) {
    for i := 0; i &lt; n; i++ {
        c := Client{i, i}
        clients &lt;- c
    }
    close(clients)
}</code></code></pre><p>The purpose of the <code>create()</code> function is to create all requests properly and then send them to the <code>clients</code> buffered channel for processing. Note that the <code>clients</code> channel is read by <code>worker()</code>.</p><pre><code><code>func main() {
    if len(os.Args) != 3 {
        fmt.Println("Need #jobs and #workers!")
        return
    }
    nJobs, err := strconv.Atoi(os.Args[1])
    if err != nil {
        fmt.Println(err)
        return
    }
    nWorkers, err := strconv.Atoi(os.Args[2])
    if err != nil {
        fmt.Println(err)
        return
    }</code></code></pre><p>In the preceding code, you read the command line parameters that define the number of jobs and workers. If the number of jobs is greater than the number of workers, the jobs are served in smaller chunks.</p><pre><code><code>    go create(nJobs)</code></code></pre><p>The <code>create()</code> call mimics the client requests that you are going to process.</p><pre><code><code>    finished := make(chan interface{})</code></code></pre><p>The <code>finished</code> channel is used for blocking the program and, therefore, needs no particular data type.</p><pre><code><code>    go func() {
        for d := range data {
            fmt.Printf("Client ID: %d\tint: ", d.job.id)
            fmt.Printf("%d\tsquare: %d\n", d.job.integer, d.square)
        }
        finished &lt;- true</code></code></pre><p>The <code>finished &lt;- true</code> statement is used for unblocking the program as soon as the <code>for</code> <code>range</code> loop ends. The <code>for range</code> loop ends when the <code>data</code> channel is closed, which happens after <code>wg.Wait()</code>, which means after all workers have finished.</p><pre><code><code>    }()
    var wg sync.WaitGroup
    for i := 0; i &lt; nWorkers; i++ {
        wg.Add(1)
        go worker(&amp;wg)
    }
    wg.Wait()
    close(data)</code></code></pre><p>The purpose of the previous <code>for</code> loop is to generate the required number of <code>worker()</code> goroutines to process all requests.</p><pre><code><code>    fmt.Printf("Finished: %v\n", &lt;-finished)
}</code></code></pre><p>The <code>&lt;-finished</code> statement in <code>fmt.Printf()</code> blocks until the <code>finished</code> channel is closed.</p><p>Running <code>wPools.go</code> creates the following kind of output:</p><pre><code><code>$ go run wPools.go 8 5
Client ID: 0    int: 0    square: 0
Client ID: 1    int: 1    square: 1
Client ID: 2    int: 2    square: 4
Client ID: 3    int: 3    square: 9
Client ID: 4    int: 4    square: 16
Client ID: 5    int: 5    square: 25
Client ID: 6    int: 6    square: 36
Finished: true</code></code></pre><p>The previous output shows that all requests were processed. This technique allows you to serve a given number of requests, which saves you from server overload. The price you pay for that is having to write more code.</p><p>The next subsection introduces signal channels and shows a technique for using them to define the order of execution for a small number of goroutines.</p><h2>Signal channels</h2><p>A signal channel is one that is used just for signaling. Put simply, you can use a signal channel when you want to inform another goroutine about something. Signal channels should not be used for data transferring. You are going to see signal channels in action in the next subsection where we specify the order of execution of goroutines.</p><h2>Specifying the order of execution for your goroutines</h2><p>This subsection presents a technique for specifying the order of execution of goroutines using signal channels. However, keep in mind that this technique works best when you are dealing with a small number of goroutines. The presented code example has four goroutines that we want to execute in the desired order&#8212;first, the goroutine for function <code>A()</code>, then function <code>B()</code>, then <code>C()</code>, and finally, <code>D()</code>.</p><p>The code of <code>defineOrder.go</code> without the <code>package</code> statement and <code>import</code> block is the following:</p><pre><code><code>var wg sync.WaitGroup
func A(a, b chan struct{}) {
    &lt;-a
    fmt.Println("A()!")
    time.Sleep(time.Second)
    close(b)
}</code></code></pre><p>Function <code>A()</code> is going to be blocked until channel <code>a</code>, which is passed as a parameter, is closed. Just before it ends, it closes channel <code>b</code>, which is passed as a parameter. This is going to unblock the next goroutine, which is going to be function <code>B()</code>.</p><pre><code><code>func B(a, b chan struct{}) {
    &lt;-a
    fmt.Println("B()!")
    time.Sleep(3 * time.Second)
    close(b)
}</code></code></pre><p>Similarly, function <code>B()</code> is going to be blocked until channel <code>a</code>, which is passed as a parameter, is closed. Just before <code>B()</code> ends, it closes channel <code>b</code>, which is passed as a parameter. As before, this is going to unblock the following function:</p><pre><code><code>func C(a, b chan struct{}) {
    &lt;-a
    fmt.Println("C()!")
    close(b)
}</code></code></pre><p>As it happened with functions <code>A()</code> and <code>B()</code>, the execution of function <code>C()</code> is blocked by channel <code>a</code>. Just before it ends, it closes channel <code>b</code>.</p><pre><code><code>func D(a chan struct{}) {
    &lt;-a
    fmt.Println("D()!")
    wg.Done()
}</code></code></pre><p>This is the last function that is going to be executed. Therefore, although it is blocked, it does not close any channels before exiting. Additionally, being the last function means that it can be executed more than once, which is not true for functions <code>A()</code>, <code>B()</code>, and <code>C()</code> because a channel can be closed only once.</p><pre><code><code>func main() {
    x := make(chan struct{})
    y := make(chan struct{})
    z := make(chan struct{})
    w := make(chan struct{})</code></code></pre><p>We need to have as many channels as the number of functions we want to execute as goroutines.</p><pre><code><code>    wg.Add(1)
    go func() {
        D(w)
    }()</code></code></pre><p>This proves that the order of execution dictated by the Go code does not matter as <code>D()</code> is going to be executed last.</p><pre><code><code>    wg.Add(1)
    go func() {
        D(w)
    }()
    go A(x, y)
    wg.Add(1)
    go func() {
        D(w)
    }()
    go C(z, w)
    go B(y, z)</code></code></pre><p>Although we run <code>C()</code> before <code>B()</code>, <code>C()</code> is going to finish after <code>B()</code> has finished.</p><pre><code><code>    wg.Add(1)
    go func() {
        D(w)
    }()
    // This triggers the process
    close(x)</code></code></pre><p>The closing of the first channel is what triggers the execution of the goroutines because this unblocks <code>A()</code>.</p><pre><code><code>    wg.Wait()
}</code></code></pre><p>Running <code>defineOrder.go</code> produces the following output:</p><pre><code><code>$ go run defineOrder.go
A()!
B()!
C()!
D()! D()! D()! D()!</code></code></pre><p>So, the four functions, which are executed as goroutines, are executed in the desired order, and, in the case of the last function, the desired number of times.</p><h1>Handling UNIX signals</h1><p>UNIX signals offer a very handy way of interacting asynchronously with applications and server processes. UNIX signal handling in Go requires the use of channels that are used exclusively for this task. The presented program handles <code>SIGINT</code> (which is called <code>syscall.SIGINT</code> in Go) and <code>SIGINFO</code> separately and uses a <code>default</code> case in a <code>switch</code> block for handling the remaining signals. The implementation of that <code>switch</code> block allows you to differentiate between the various signals according to your needs.</p><p>There exists a dedicated channel that receives all signals, as defined by the <code>signal.Notify()</code> function. Go channels can have a capacity&#8212;the capacity of this particular channel is 1 in order to be able to receive and keep <em><strong>one signal at a time</strong></em>. This makes perfect sense as a signal can terminate a program and there is no need to try to handle another signal at the same time. There is usually an anonymous function that is executed as a goroutine and performs the signal handling and nothing else. The main task of that goroutine is to listen to the channel for data. Once a signal is received, it is sent to that channel, read by the goroutine, and stored into a variable&#8212;at this point, the channel can receive more signals. That variable is processed by a <code>switch</code> statement.</p><p>Some signals cannot be caught, and the operating system cannot ignore them. So, the <code>SIGKILL</code> and <code>SIGSTOP</code> signals cannot be blocked, caught, or ignored; the reason for this is that they allow privileged users as well as the UNIX kernel to terminate any process they desire.</p><p>Create a text file by typing the following code&#8212;a good filename for it would be <code>signals.go</code>.</p><pre><code><code>package main
import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"
)
func handleSignal(sig os.Signal) {
    fmt.Println("handleSignal() Caught:", sig)
}</code></code></pre><p><code>handleSignal()</code> is a separate function for handling signals. However, you can also handle signals inline, in the branches of a <code>switch</code> statement.</p><pre><code><code>func main() {
    fmt.Printf("Process ID: %d\n", os.Getpid())
    sigs := make(chan os.Signal, 1)</code></code></pre><p>We create a channel with data of the type <code>os.Signal</code> because all channels must have a type.</p><pre><code><code>    signal.Notify(sigs)</code></code></pre><p>The previous statement means handling all signals that can be handled.</p><pre><code><code>    start := time.Now()
    go func() {
        for {
            sig := &lt;-sigs</code></code></pre><p>Wait until you read data (&lt;-) from the <code>sigs</code> channel and store it in the <code>sig</code> variable.</p><pre><code><code>            switch sig {</code></code></pre><p>Depending on the read value, act accordingly. This is how you differentiate between signals.</p><pre><code><code>            case syscall.SIGINT:
                duration := time.Since(start)
                fmt.Println("Execution time:", duration)</code></code></pre><p>For the handling of <code>syscall.SIGINT</code>, we calculate the time that has passed since the beginning of the program execution and print it on the screen.</p><pre><code><code>            case syscall.SIGINFO:
                handleSignal(sig)</code></code></pre><p>The code of the <code>syscall.SIGINFO</code> case calls the <code>handleSignal()</code> function&#8212;it is up to the developer to decide on the details of the implementation.</p><p>On Linux machines, you should replace <code>syscall.SIGINFO</code> with another signal such as <code>syscall.SIGUSR1</code> or <code>syscall.SIGUSR2</code> because <code>syscall.SIGINFO</code> is not available on Linux (<a href="https://github.com/golang/go/issues/1653">https://github.com/golang/go/issues/1653</a>).</p><pre><code><code>                // do not use return here because the goroutine exits
                // but the time.Sleep() will continue to work!
                os.Exit(0)
            default:
                fmt.Println("Caught:", sig)
            }</code></code></pre><p>If there is no match, the <code>default</code> case handles the rest of the values and just prints a message.</p><pre><code><code>        }
    }()
    for {
        fmt.Print("+")
        time.Sleep(10 * time.Second)
    }
}</code></code></pre><p>The endless for loop at the end of the <code>main()</code> function is for emulating the operation of a real program. Without an endless <code>for</code> loop, the program exits almost immediately.</p><p>Running <code>signals.go</code> and interacting with it creates the following kind of output:</p><pre><code><code>$ go run signals.go
Process ID: 70153
+^CExecution time: 631.533125ms
+Caught: user defined signal 1
+Caught: urgent I/O condition
+signal: killed</code></code></pre><p>The second line of output was generated by pressing <em>Ctrl</em> + <em>C</em> on the keyboard, which, on UNIX machines, sends the <code>syscall.SIGINT</code> signal to the program. The third line of output was caused by executing <code>kill -USR1 74252</code> on a different terminal. The last line of the output was generated by the <code>kill -9 74252</code> command. As the <code>KILL</code> signal (which is also represented by the number 9) cannot be handled, it terminates the program, and the shell prints the <code>killed</code> message.</p><h2>Handling two signals</h2><p>If you want to handle a limited number of signals instead of all of them, you should replace the <code>signal.Notify(sigs)</code> statement with a statement like the following:</p><pre><code><code>signal.Notify(sigs, syscall.SIGINT, syscall.SIGINFO)</code></code></pre><p>After that, you need to make the appropriate changes to the code of the goroutine responsible for signal handling in order to identify and handle <code>syscall.SIGINT</code> and <code>syscall.SIGINFO</code>&#8212;the current version (<code>signals.go</code>) already handles both of them.</p><p>The next section talks about shared memory and shared variables, which is a very handy way of making goroutines communicate with each other with the use of channels.</p><div><hr></div><h1>Shared memory and shared variables</h1><p>Shared memory and shared variables are huge topics in concurrent programming and the most common ways for UNIX threads to communicate with each other. The same principles apply to Go and goroutines, which is what this section is about. <em><strong>A mutex variable, which is the abbreviation for a mutual exclusion variable, is mainly used for thread synchronization and for protecting shared data when multiple writes or a write and a read can occur at the same time</strong></em>. A mutex works like a buffered channel with a capacity of one, which allows, at most, one goroutine to access a shared variable at any given time. This means that there is no way for two or more goroutines to be able to update that variable simultaneously. Go offers the <code>sync.Mutex</code> and <code>sync.RWMutex</code> data types.</p><p>A <em>critical section</em> of a concurrent program is the code that cannot be executed simultaneously by all processes, threads, or, in this case, goroutines. It is the code that needs to be protected by mutexes. Therefore, identifying the critical sections of your code makes the whole programming process so much simpler that you should pay particular attention to this task. A critical section cannot be embedded into another critical section when both critical sections use the same <code>sync.Mutex</code> or <code>sync.RWMutex</code> variable. However, <em><strong>avoid at almost any cost the spreading of mutexes across functions because that makes it really hard to see whether you are embedding or not</strong></em>.</p><h2>The sync.Mutex type</h2><p>The <code>sync.Mutex</code> type is the Go implementation of a mutex. Its definition, which can be found in the <code>mutex.go</code> file of the <code>sync</code> directory, is as follows (you do not need to know the definition of <code>sync.Mutex</code> in order to use it):</p><pre><code><code>type Mutex struct {
    state int32
    sema  uint32
}</code></code></pre><p>The definition of <code>sync.Mutex</code> is nothing special. All of the interesting work is done by the <code>sync.Lock()</code> and <code>sync.Unlock()</code> functions, which can lock and unlock a <code>sync.Mutex</code> variable, respectively. Locking a mutex means that nobody else can lock it until it has been released using the <code>sync.Unlock()</code> function. All these are illustrated in <code>mutex.go</code>, which contains the following code:</p><pre><code><code>package main
import (
    "fmt"
    "os"
    "strconv"
    "sync"
    "time"
)
var m sync.Mutex
var v1 int
func change() {
    m.Lock()
    defer m.Unlock()</code></code></pre><p>This function makes changes to the value of <code>v1</code>. The critical section begins here.</p><pre><code><code>    time.Sleep(time.Second)
    v1 = v1 + 1
    if v1 == 10 {
        v1 = 0
        fmt.Print("* ")
    }</code></code></pre><p>This is the end of the critical section. Now, another goroutine can lock the mutex.</p><pre><code><code>}
func read() int {
    m.Lock()
    a := v1
    defer m.Unlock()
    return a
}</code></code></pre><p>This function is used for reading the value of <code>v1</code>&#8212;therefore, it should use a mutex to make the process concurrently safe. Most specifically, we want to make sure that nobody is going to change the value of <code>v1</code> while we are reading it. The rest of the program contains the implementation of the <code>main()</code> function&#8212;feel free to see the entire code of <code>mutex.go</code> in the GitHub repository of the book.</p><p>Running <code>mutex.go</code> produces the following output:</p><pre><code><code>$ go run -race mutex.go 10
0 -&gt; 1-&gt; 2-&gt; 3-&gt; 4-&gt; 5-&gt; 6-&gt; 7-&gt; 8-&gt; 9* -&gt; 0-&gt; 0</code></code></pre><p>The previous output shows that due to the use of a mutex, goroutines cannot access shared data and, therefore, there are no hidden race conditions.</p><p>The next subsection shows what could happen if we forget to unlock a mutex.</p><h2>What happens if you forget to unlock a mutex?</h2><p>Forgetting to unlock a <code>sync.Mutex</code> mutex creates a panic situation, even in the simplest kind of a program. The same applies to the <code>sync.RWMutex</code> mutex, which is presented in the next section. Let us now see a code example to understand this unpleasant situation a lot better&#8212;this is part of <code>forgetMutex.go</code>:</p><pre><code><code>var m sync.Mutex
var w sync.WaitGroup
func function() {
    m.Lock()
    fmt.Println("Locked!")
}</code></code></pre><p>Here, we lock a mutex without releasing it afterward. This means that if we run <code>function()</code> as a goroutine more than once, all instances after the first one are going to be blocked waiting to <code>Lock()</code> the shared mutex. In our case, we run two goroutines&#8212;feel free to see the entire code of <code>forgetMutex.go</code> for more details. Running <code>forgetMutex.go</code> generates the following output:</p><pre><code><code>Locked!
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x140000021a0?)
    /opt/homebrew/Cellar/go/1.21.0/libexec/src/runtime/sema.go:62 +0x2c
sync.(*WaitGroup).Wait(0x100fa1710)
    /opt/homebrew/Cellar/go/1.21.0/libexec/src/sync/waitgroup.go:116 +0x74
main.main()
    ~/go/src/github.com/mactsouk/mGo4th/ch08/forgetMutex.go:29 +0x5c
goroutine 34 [sync.Mutex.Lock]:
sync.runtime_SemacquireMutex(0x0?, 0x0?, 0x0?)
    /opt/homebrew/Cellar/go/1.21.0/libexec/src/runtime/sema.go:77 +0x28
sync.(*Mutex).lockSlow(0x100fa1520)
    /opt/homebrew/Cellar/go/1.21.0/libexec/src/sync/mutex.go:171 +0x174
sync.(*Mutex).Lock(...)
    /opt/homebrew/Cellar/go/1.21.0/libexec/src/sync/mutex.go:90
main.function()
    ~/go/src/github.com/mactsouk/mGo4th/ch08/forgetMutex.go:12 +0x84
main.main.func1()
    ~/go/src/github.com/mactsouk/mGo4th/ch08/forgetMutex.go:20 +0x50
created by main.main in goroutine 1
    ~/go/src/github.com/mactsouk/mGo4th/ch08/forgetMutex.go:18 +0x34
exit status 2</code></code></pre><p>As expected, the program crashes because of the deadlock. To avoid such situations, always remember to unlock any mutexes created in your program as soon as possible.</p><p>Let us now discuss <code>sync.RWMutex</code>, which is an improved version of <code>sync.Mutex</code>.</p><h2>The sync.RWMutex type</h2><p>The <code>sync.RWMutex</code> data type is an improved version of <code>sync.Mutex</code> and is defined in the <code>rwmutex.go</code> file of the <code>sync</code> directory of the Go Standard library as follows:</p><pre><code><code>type RWMutex struct {
    w           Mutex
    writerSem   uint32
    readerSem   uint32
    readerCount int32
    readerWait  int32
}</code></code></pre><p>In other words, <code>sync.RWMutex</code> is based on <code>sync.Mutex</code> with the necessary additions and improvements. So, you might ask, how does <code>sync.RWMutex</code> improve <code>sync.Mutex</code>? Although a single function is allowed to perform write operations with a <code>sync.RWMutex</code> mutex, you can have multiple readers owning a <code>sync.RWMutex</code> mutex&#8212;this means that read operations are usually faster with <code>sync.RWMutex</code>. However, there is one important detail that you should be aware of: until all of the readers of a <code>sync.RWMutex</code> mutex unlock it, you cannot lock it for writing, which is the small price you have to pay for the performance improvement you get for allowing multiple readers.</p><p>The functions that can help you work with <code>sync.RWMutex</code> are <code>RLock()</code> and <code>RUnlock()</code>, which are used for locking and unlocking the mutex for reading purposes, respectively. The <code>Lock()</code> and <code>Unlock()</code> functions used in <code>sync.Mutex</code> should still be used when you want to lock and unlock a <code>sync.RWMutex</code> mutex for writing purposes. Finally, it should be apparent that you should not make changes to any shared variables inside an <code>RLock()</code> and <code>RUnlock()</code> block of code.</p><p>All these are illustrated in <code>rwMutex.go</code>&#8212;the important code is the following:</p><pre><code><code>var Password *secret
var wg sync.WaitGroup
type secret struct {
    RWM      sync.RWMutex
    password string
}</code></code></pre><p>This is the shared variable of the program&#8212;you can share any type of variable you want.</p><pre><code><code>func Change(pass string) {
    if Password == nil {
        fmt.Println("Password is nil!")
        return
    }
    fmt.Println("Change() function")
    Password.RWM.Lock()</code></code></pre><p>This is the beginning of the critical section.</p><pre><code><code>    fmt.Println("Change() Locked")
    time.Sleep(4 * time.Second)
    Password.password = pass
    Password.RWM.Unlock()</code></code></pre><p>This is the end of the critical section.</p><pre><code><code>    fmt.Println("Change() UnLocked")
}</code></code></pre><p>The <code>Change()</code> function makes changes to the shared variable <code>Password</code> and, therefore, needs to use the <code>Lock()</code> function, which can be held by a single writer only.</p><pre><code><code>func show () {
    defer wg.Done()
    defer Password.RWM.RUnlock()
    Password.RWM.RLock()
    fmt.Println("Show function locked!")
    time.Sleep(2 * time.Second)
    fmt.Println("Pass value:", Password.password)
}</code></code></pre><p>The <code>show()</code> function reads the shared variable <code>Password</code> and therefore it is allowed to use the <code>RLock()</code> function, which can be held by multiple readers. Inside <code>main()</code>, three <code>show()</code> functions are executed as goroutines before a call to the <code>Change()</code> function, which also runs as a goroutine. The key point here is that no race conditions are going to happen. Running <code>rwMutex.go</code> produces the following output:</p><pre><code><code>$ go run rwMutex.go
Change() function</code></code></pre><p>The <code>Change()</code> function is executed but cannot acquire the mutex because it is already taken by one or more <code>show()</code> goroutines.</p><pre><code><code>Show function locked!
Show function locked!</code></code></pre><p>The previous output verifies that two <code>show()</code> goroutines have successfully taken the mutex for reading.</p><pre><code><code>Change() function</code></code></pre><p>Here, we can see a second <code>Change()</code> function running and waiting to get the mutex.</p><pre><code><code>Pass value: myPass
Pass value: myPass</code></code></pre><p>This is the output from the two <code>show()</code> goroutines.</p><pre><code><code>Change() Locked
Change() UnLocked</code></code></pre><p>Here we see that one <code>Change()</code> goroutine finishes its job.</p><pre><code><code>Show function locked!
Pass value: 54321</code></code></pre><p>After that another <code>show()</code> goroutine finishes.</p><pre><code><code>Change() Locked
Change() UnLocked
Current password value: 123456</code></code></pre><p>Lastly, the second <code>Change()</code> goroutine finishes. The last output line is for making sure that the password value has changed&#8212;please look at the full code of <code>rwMutex.go</code> for more details.</p><p>Bear in mind that the output you are going to get might be different due to the way the scheduler works. This is the nature of concurrent programming and these programs do not have any mechanism to ensure the <code>show()</code> function should be scheduled first.</p><p>The next subsection discusses the use of the <code>atomic</code> package for avoiding race conditions.</p><h2>The atomic package</h2><p>An atomic operation is an operation that is completed in a single step relative to other threads or, in this case, to other goroutines. <em><strong>This means that an atomic operation cannot be interrupted in the middle of it.</strong></em> The Go Standard library offers the <code>atomic</code> package, which, in some simple cases, can help you avoid using a mutex. With the <code>atomic</code> package, you can have atomic counters accessed by multiple goroutines without synchronization issues and without worrying about race conditions. However, mutexes are more versatile than atomic operations.</p><p>As illustrated in the code that follows, when using an atomic variable, all reading and writing operations of an atomic variable must be done using the functions provided by the <code>atomic</code> package in order to avoid race conditions.</p><p>The code in <code>atomic.go</code> is as follows, which is made smaller by hardcoding some values:</p><pre><code><code>package main
import (
    "fmt"
    "sync"
    "sync/atomic"
)
type atomCounter struct {
    val int64
}</code></code></pre><p>This is a structure for holding the desired <code>int64</code> atomic variable.</p><pre><code><code>func (c *atomCounter) Value() int64 {
    return atomic.LoadInt64(&amp;c.val)
}</code></code></pre><p>This is a helper function that returns the current value of an <code>int64</code> atomic variable using <code>atomic.LoadInt64()</code>.</p><pre><code><code>func main() {
    X := 100
    Y := 4
    var waitGroup sync.WaitGroup
    counter := atomCounter{}
    for i := 0; i &lt; X; i++ {</code></code></pre><p>We are creating lots of goroutines that change the shared variable&#8212;as stated before, the use of the <code>atomic</code> package for working with the shared variable offers a simple way of avoiding race conditions when changing the value of the shared variable.</p><pre><code><code>        waitGroup.Add(1)
        go func() {
            defer waitGroup.Done()
            for i := 0; i &lt; Y; i++ {
                atomic.AddInt64(&amp;counter.val, 1)
            }</code></code></pre><p>The <code>atomic.AddInt64()</code> function changes the value of the <code>val</code> field of the <code>counter</code> structure variable in a safe way.</p><pre><code><code>        }()
    }
    waitGroup.Wait()
    fmt.Println(counter.Value())
}</code></code></pre><p>Running <code>atomic.go</code> while checking for race conditions produces the following kind of output:</p><pre><code><code>$ go run -race atomic.go
400</code></code></pre><p>So, the atomic variable is modified by multiple goroutines without any issues.</p><p>The next subsection shows how to share memory using goroutines.</p><h2>Sharing memory using goroutines</h2><p>This subsection illustrates how to share data using a dedicated goroutine. Although shared memory is the traditional way that threads communicate with each other, Go comes with built-in synchronization features that allow a single goroutine to own a shared piece of data. This means that other goroutines must send messages to this single goroutine that owns the shared data, which prevents the corruption of the data. Such a goroutine is called a monitor goroutine. In Go terminology, <em><strong>this is sharing by communicating instead of communicating by sharing</strong></em>.</p><p>Personally, I prefer to use a monitor goroutine instead of traditional shared memory techniques because the implementation with the monitor goroutine is safer, closer to the Go philosophy, and easier to understand.</p><p>The logic of the program can be found in the implementation of the <code>monitor()</code> function. More specifically, the <code>select</code> statement orchestrates the operation of the entire program. When you have a read request, the <code>read()</code> function attempts to read from the <code>readValue</code> channel, which is controlled by the <code>monitor()</code> function.</p><p>This returns the current value of the value variable. On the other hand, when you want to change the stored value, you call <code>set()</code>. This writes to the <code>writeValue</code> channel, which is also handled by the same <code>select</code> statement. As a result, no one can deal with the shared variable without using the <code>monitor()</code> function, which is in charge.</p><p>The code of <code>monitor.go</code> is as follows:</p><pre><code><code>package main
import (
    "fmt"
    "math/rand"
    "os"
    "strconv"
    "sync"
    "time"
)
var readValue = make(chan int)
var writeValue = make(chan int)
func set(newValue int) {
    writeValue &lt;- newValue
}</code></code></pre><p>This function sends data to the <code>writeValue</code> channel.</p><pre><code><code>func read() int {
    return &lt;-readValue
}</code></code></pre><p>When the <code>read()</code> function is called, it reads from the <code>readValue</code> channel&#8212;this reading happens inside the <code>monitor()</code> function.</p><pre><code><code>func monitor() {
    var value int
    for {
        select {
        case newValue := &lt;-writeValue:
            value = newValue
            fmt.Printf("%d ", value)
        case readValue &lt;- value:
        }
    }
}</code></code></pre><p>The <code>monitor()</code> function contains the logic of the program with the endless <code>for</code> loop and the <code>select</code> statement. The first case receives data from the <code>writeValue</code> channel, sets the <code>value</code> variable accordingly, and prints that new value. The second case sends the value of the <code>value</code> variable to the <code>readValue</code> channel. As all traffic goes through <code>monitor()</code> and its <code>select</code> block, there is no way to have a race condition because there is a single instance of <code>monitor()</code> running.</p><pre><code><code>func main() {
    if len(os.Args) != 2 {
        fmt.Println("Please give an integer!")
        return
    }
    n, err := strconv.Atoi(os.Args[1])
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("Going to create %d random numbers.\n", n)
    rand.Seed(time.Now().Unix())
    go monitor()</code></code></pre><p>It is important that the <code>monitor()</code> function is executed first because that is the goroutine that orchestrates the flow of the program.</p><pre><code><code>    var wg sync.WaitGroup
    for r := 0; r &lt; n; r++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            set(rand.Intn(10 * n))
        }()
    }</code></code></pre><p>When the <code>for</code> loop ends, it means that we have created the desired number of random numbers.</p><pre><code><code>    wg.Wait()
    fmt.Printf("\nLast value: %d\n", read())
}</code></code></pre><p>Lastly, we wait for all <code>set()</code> goroutines to finish before printing the last random number.</p><p>Running <code>monitor.go</code> produces the following output:</p><pre><code><code>$ go run monitor.go 10
Going to create 10 random numbers.
98 22 5 84 20 26 45 36 0 16 
Last value: 16</code></code></pre><p>So, 10 random numbers are created by 10 goroutines and all these goroutines send their output to the <code>monitor()</code> function, which is also executed as a goroutine. Apart from receiving the results, the <code>monitor()</code> function prints them on the screen, so all this output is generated by <code>monitor()</code>.</p><p>The next section discusses the <code>go</code> statement in more detail.</p><div><hr></div><h1>Closured variables and the go statement</h1><p>In this section, we are going to talk about closured variables, which are variables inside closures, and the <code>go</code> statement. Notice that closured variables in goroutines are evaluated when the goroutine actually runs and when the <code>go</code> statement is executed in order to create a new goroutine. This means that closured variables are going to be replaced by their values when the Go scheduler decides to execute the relevant code. This is illustrated in the <code>main()</code> function of <code>goClosure.go</code>:</p><pre><code><code>func main() {
    for i := 0; i &lt;= 20; i++ {
        go func() {
            fmt.Print(i, " ")
        }()
    }
    time.Sleep(time.Second)
    fmt.Println()
}</code></code></pre><p>Running <code>goClosure.go</code> produces the following output:</p><pre><code><code>$ go run goClosure.go 
3 7 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21</code></code></pre><p>The program mostly prints the number 21, which is the last value of the variable of the <code>for</code> loop, and not the other numbers. As <code>i</code> is a closured variable, it is evaluated at the time of execution. As the goroutines begin but wait for the Go scheduler to allow them to get executed, the <code>for</code> loop ends, so the value of <code>i</code> that is being used is 21. Lastly, the same issue also applies to Go channels, so be careful.</p><p>Running <code>goClosure.go</code> with the Go race detector reveals the issue:</p><pre><code><code>$ go run -race goClosure.go
5 4 5 5 ==================
WARNING: DATA RACE
Read at 0x00c00011e028 by goroutine 6:
  main.main.func1()
      ~/go/src/github.com/mactsouk/mGo4th/ch08/goClosure.go:11 +0x34
Previous write at 0x00c00011e028 by main goroutine:
  main.main()
      ~/go/src/github.com/mactsouk/mGo4th/ch08/goClosure.go:9 +0x5c
Goroutine 6 (running) created at:
  main.main()
      ~/go/src/github.com/mactsouk/mGo4th/ch08/goClosure.go:10 +0x44
==================
8 8 6 10 12 11 15 15 15 18 20 20 21 15 21 21 21
Found 1 data race(s)
exit status 66</code></code></pre><p>Now, let us correct <code>goClosure.go</code> and present it to you&#8212;the new name is <code>goClosureCorrect.go</code> and its <code>main()</code> function is as follows:</p><pre><code><code>func main() {
    for i := 0; i &lt;= 20; i++ {
        i := i
        go func() {
            fmt.Print(i, " ")
        }()
    }</code></code></pre><p>This is one way of correcting the issue. The valid yet bizarre <code>i := i</code> statement creates a new instance of the variable for the goroutine that holds the correct value. Although this is a valid approach, this kind of variable shadowing is not considered a good practice.</p><p>Variable shadowing in Go occurs when a variable declared in a nested scope has the same name as a variable in an outer scope. While variable shadowing can be intentional and useful in certain situations, it can also lead to confusion and introduce subtle bugs. In practice, it is recommended to avoid unnecessary variable shadowing and choose meaningful variable names that minimize the likelihood of unintentional shadowing.</p><pre><code><code>    time.Sleep(time.Second)
    fmt.Println()
    for i := 0; i &lt;= 20; i++ {
        go func(x int) {
            fmt.Print(x, " ")
        }(i)
    }</code></code></pre><p>This is a totally different way of correcting the race condition: pass the current value of <code>i</code> to the anonymous function as a parameter and everything is OK. As explained in <em>Chapter 15</em>, <em>Changes in Recent Go Versions</em>, this issue does not exist in Go 1.22.</p><pre><code><code>    time.Sleep(time.Second)
    fmt.Println()
}</code></code></pre><p>Testing <code>goClosureCorrect.go</code> with the race detector generates the expected output:</p><pre><code><code>$ go run -race goClosureCorrect.go
0 1 2 4 3 5 6 9 8 7 10 11 13 12 14 16 15 17 18 20 19
0 1 2 3 4 5 6 7 8 10 9 12 13 11 14 15 16 17 18 19 20</code></code></pre><p>The next section presents the functionality of the <code>context</code> package.</p><div><hr></div><h1>The context package</h1><p>The main purpose of the <code>context</code> package is to define the <code>Context</code> type and support cancellation. Yes, you heard that right; there are times when, for some reason, you want to abandon what you are doing. However, it would be very helpful to be able to include some extra information about your cancellation decisions. The <code>context</code> package allows you to do exactly that.</p><p>If you take a look at the source code of the <code>context</code> package, you will realize that its implementation is pretty simple&#8212;even the implementation of the <code>Context</code> type is pretty simple, yet the <code>context</code> package is very important.</p><p>The <code>Context</code> type is an interface with four methods: <code>Deadline()</code>, <code>Done()</code>, <code>Err()</code>, and <code>Value()</code>. The good news is that you do not need to implement all of these functions of the <code>Context</code> interface&#8212;you just need to modify a <code>Context</code> variable using methods such as <code>context.WithCancel()</code>, <code>context.WithDeadline()</code>, and <code>context.WithTimeout()</code>.</p><p>All three of these methods return a derived <code>Context</code> (the child) and a <code>CancelFunc()</code> function. Calling the <code>CancelFunc()</code> function removes the parent&#8217;s reference to the child and stops any associated timers. As a side effect, this means that the Go garbage collector is free to garbage collect the child goroutines that no longer have associated parent goroutines. For garbage collection to work correctly, the parent goroutine needs to keep a reference to each child goroutine. If a child goroutine ends without the parent knowing about it, then a memory leak occurs until the parent is canceled as well.</p><p>The example that follows showcases the use of the <code>context</code> package. The program contains four functions, including the <code>main()</code> function. Functions <code>f1()</code>, <code>f2()</code>, and <code>f3()</code> each require just one parameter (which is a time delay) because everything else they need is defined inside their function body. In this example, we use <code>context.Background()</code> to initialize an empty <code>Context</code>. The other function that can create an empty <code>Context</code> is <code>context.TODO()</code>, which is presented later on in this chapter.</p><pre><code><code>package main
import (
    "context"
    "fmt"
    "os"
    "strconv"
    "time"
)
func f1(t int) {
    c1 := context.Background()
    c1, cancel := context.WithCancel(c1)
    defer cancel()</code></code></pre><p>The <code>WithCancel()</code> method returns a copy of the parent context with a new <code>Done</code> channel. Notice that the <code>cancel</code> variable, which is a function, is one of the return values of <code>context.CancelFunc()</code>. The <code>context.WithCancel()</code> function uses an existing <code>Context</code> and creates a child with cancellation. The <code>context.WithCancel()</code> function also returns a <code>Done</code> channel that can be closed, either when the <code>cancel()</code> function is called, as shown in the preceding code, or when the <code>Done</code> channel of the parent context is closed.</p><pre><code><code>    go func() {
        time.Sleep(4 * time.Second)
        cancel()
    }()
    select {
    case &lt;-c1.Done():
        fmt.Println("f1() Done:", c1.Err())
        return
    case r := &lt;-time.After(time.Duration(t) * time.Second):
        fmt.Println("f1():", r)
    }
    return
}</code></code></pre><p>The <code>f1()</code> function creates and executes a goroutine. The <code>time.Sleep()</code> call simulates the time it would take a real goroutine to do its job. In this case, it is 4 seconds, but you can put any time period you want. If the <code>c1</code> context calls the <code>Done()</code> function in less than 4 seconds, the goroutine will not have enough time to finish.</p><pre><code><code>func f2(t int) {
    c2 := context.Background()
    c2, cancel := context.WithTimeout(c2, time.Duration(t)*time.Second)
    defer cancel()</code></code></pre><p>The cancel variable in <code>f2()</code> comes from <code>context.WithTimeout()</code>, which requires two parameters: a <code>Context</code> parameter and a <code>time.Duration</code> parameter. When the timeout period expires, the <code>cancel()</code> function is called automatically.</p><pre><code><code>    go func() {
        time.Sleep(4 * time.Second)
        cancel()
    }()
    select {
    case &lt;-c2.Done():
        fmt.Println("f2() Done:", c2.Err())
        return
    case r := &lt;-time.After(time.Duration(t) * time.Second):
        fmt.Println("f2():", r)
    }
    return
}
func f3(t int) {
    c3 := context.Background()
    deadline := time.Now().Add(time.Duration(2*t) * time.Second)
    c3, cancel := context.WithDeadline(c3, deadline)
    defer cancel()</code></code></pre><p>The <code>cancel</code> variable in <code>f3()</code> comes from <code>context.WithDeadline()</code>, which requires two parameters: a <code>Context</code> variable and a time in the future that signifies the deadline of the operation. When the deadline passes, the <code>cancel()</code> function is called automatically.</p><pre><code><code>    go func() {
        time.Sleep(4 * time.Second)
        cancel()
    }()
    select {
    case &lt;-c3.Done():
        fmt.Println("f3() Done:", c3.Err())
        return
    case r := &lt;-time.After(time.Duration(t) * time.Second):
        fmt.Println("f3():", r)
    }
    return
}</code></code></pre><p>The logic of <code>f3()</code> is the same as in <code>f1()</code> and <code>f2()</code>&#8212;the <code>select</code> block orchestrates the process.</p><pre><code><code>func main() {
    if len(os.Args) != 2 {
        fmt.Println("Need a delay!")
        return
    }
    delay, err := strconv.Atoi(os.Args[1])
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("Delay:", delay)
    f1(delay)
    f2(delay)
    f3(delay)
}</code></code></pre><p>The three functions are executed in sequence by the <code>main()</code> function. Running <code>useContext.go</code> produces the following kind of output:</p><pre><code><code>$ go run useContext.go 3
Delay: 3
f1(): 2023-08-28 16:23:22.300595 +0300 EEST m=+3.001225751
f2(): 2023-08-28 16:23:25.302122 +0300 EEST m=+6.002730959
f3(): 2023-08-28 16:23:28.303326 +0300 EEST m=+9.00391262</code></code></pre><p>The long lines of the output are the return values of <code>time.After()</code> and show the times that <code>After()</code> sent the current time on the returned channel. All of them denote a normal operation of the program.</p><p>If you define a bigger delay, then the output is going to be similar to the following:</p><pre><code><code>$ go run useContext.go 13
Delay: 13
f1() Done: context canceled
f2() Done: context canceled
f3() Done: context canceled</code></code></pre><p>The point here is that the operation of the program is canceled when there are delays in its execution.</p><h2>About context.WithCancelCause</h2><p>The <code>context.WithCancelCause()</code> method was introduced in Go 1.21. Its main advantage is that it gives you customization capabilities, which are not offered by the other methods of the <code>context</code> package. Apart from that, it behaves like <code>WithCancel()</code>.</p><p>Similar to <code>context.WithCancelCause()</code>, there exists <code>context.WithTimeoutCause()</code> and <code>context.WithDeadlineCause()</code>.</p><p>The <code>withCancelCause.go</code> program illustrates the use of <code>context.WithCancelCause()</code>.</p><pre><code><code>func main() {
    ctx := context.Background()
    ctx, cancel := context.WithCancelCause(ctx)
    cancel(errors.New("Canceled by timeout"))
    err := takingTooLong(ctx)
    if err != nil {
        fmt.Println(err)
        return
    }
}</code></code></pre><p>The implementation of <code>main()</code> contains two important elements. First, we call <code>context.WithCancelCause()</code>, which returns a context, and a <code>CancelCauseFunc()</code> function, which behaves like <code>CancelFunc()</code> while allowing us to define and customize the cancellation cause giving clearer context to the error situation&#8212;in this case, the cancellation cause is defined as <code>errors.New("Canceled by timeout")</code>. After that, we call <code>takingTooLong()</code> with the context that we have just defined. If <code>takingTooLong()</code> returns an error that is not <code>nil</code>, we print that error.</p><pre><code><code>func takingTooLong(ctx context.Context) error {
    select {
    case &lt;-time.After(3 * time.Second):
        fmt.Println("Done!")
        return nil
    case &lt;-ctx.Done():
        fmt.Println("Canceled!")
        return context.Cause(ctx)
    }
}</code></code></pre><p>The previous function returns either <code>nil</code> or <code>context.Cause(ctx)</code>.</p><p>Running <code>withCancelCause.go</code> produces the following output:</p><pre><code><code>$ go run withCancelCause.go
Canceled!
Canceled by timeout
</code></code></pre><p>So, as the second part of the <code>select</code> block is executed, <code>takingTooLong()</code> prints <code>Canceled!</code> and <code>main()</code> prints the cancellation reason according to the initialization of <code>context.WithCancelCause()</code>.</p><p>We are not completely done with <code>context</code> as the next chapter is going to use it to timeout HTTP interactions on the client side of the connection. The next section discusses the <code>semaphore</code> package, which is not part of the standard library.</p><div><hr></div><h1>The semaphore package</h1><p>This last section of this chapter presents the <code>semaphore</code> package, which is provided by the Go team. A semaphore is a construct that can limit or control the access to a shared resource. As we are talking about Go, <em><strong>a semaphore can limit the access of goroutines to a shared resource</strong></em> but, originally, semaphores were used for limiting access to threads. Semaphores can have weights that limit the number of threads or goroutines that can have access to a resource.</p><p>The process is supported via the <code>Acquire()</code> and <code>Release()</code> methods, which are defined as follows:</p><pre><code><code>func (s *Weighted) Acquire(ctx context.Context, n int64) error
func (s *Weighted) Release(n int64)</code></code></pre><p>The second parameter of <code>Acquire()</code> defines the weight of the semaphore. As we are going to use an external package, we need to put the code inside <code>~/go/src</code> in order to use Go modules: <code>~/go/src/github.com/mactsouk/mGo4th/ch08/semaphore</code>.</p><p>Now, let us present the code of <code>semaphore.go</code>, which shows an implementation of a worker pool using semaphores:</p><pre><code><code>package main
import (
    "context"
    "fmt"
    "os"
    "strconv"
    "time"
    "golang.org/x/sync/semaphore"
)
var Workers = 4</code></code></pre><p>The <code>Workers</code> variable specifies the maximum number of goroutines that can be executed by this program.</p><pre><code><code>var sem = semaphore.NewWeighted(int64(Workers))</code></code></pre><p>This is where we define the semaphore with a weight identical to the maximum number of goroutines that can be executed concurrently. This means that no more than <code>Workers</code> goroutines can acquire the semaphore at the same time.</p><pre><code><code>func worker(n int) int {
    square := n * n
    time.Sleep(time.Second)
    return square
}</code></code></pre><p>The <code>worker()</code> function is run as part of a goroutine. However, as we are using a semaphore, there is no need to return the results to a channel.</p><pre><code><code>func main() {
    if len(os.Args) != 2 {
        fmt.Println("Need #jobs!")
        return
    }
    nJobs, err := strconv.Atoi(os.Args[1])
    if err != nil {
        fmt.Println(err)
        return
    }</code></code></pre><p>The previous code reads the number of jobs that we want to run.</p><pre><code><code>    // Where to store the results
    var results = make([]int, nJobs)
    // Needed by Acquire()
    ctx := context.TODO()
    for i := range results {
        err = sem.Acquire(ctx, 1)
        if err != nil {
            fmt.Println("Cannot acquire semaphore:", err)
            break
        }</code></code></pre><p>In this part, we try to acquire the semaphore as many times as the number of jobs defined by <code>nJobs</code>. If <code>nJobs</code> is bigger than <code>Workers</code>, then the <code>Acquire()</code> call is going to block and wait for <code>Release()</code> calls in order to unblock.</p><pre><code><code>        go func(i int) {
            defer sem.Release(1)
            temp := worker(i)
            results[i] = temp
        }(i)
    }</code></code></pre><p>This is where we run the goroutines that do the job and write the results to the <code>results</code> slice. As each goroutine writes to a different slice element, there are not any race conditions.</p><pre><code><code>    err = sem.Acquire(ctx, int64(Workers))
    if err != nil {
        fmt.Println(err)
    }</code></code></pre><p>This is a clever trick: we acquire all of the tokens so that the <code>sem.Acquire()</code> call blocks until all workers/goroutines have finished. This is similar in functionality to a <code>Wait()</code> call.</p><pre><code><code>    for k, v := range results {
        fmt.Println(k, "-&gt;", v)
    }
}</code></code></pre><p>The last part of the program is about printing the results. After writing the code, we need to run the following commands to get the required Go modules:</p><pre><code><code>$ go mod init
$ go mod tidy
$ mod download golang.org/x/sync</code></code></pre><p>Apart from the first command, these commands were indicated by the output of <code>go mod init</code>, so you do not have to remember anything.</p><p>Lastly, running <code>semaphore.go</code> produces the following output:</p><pre><code><code>$ go run semaphore.go 3
0 -&gt; 0
1 -&gt; 1
2 -&gt; 4</code></code></pre><p>Each line in the output shows the input value and the output value separated by -&gt;. The use of the semaphore keeps things in order.</p><div><hr></div><h1>Making the statistics application concurrent</h1><p>In this section of this chapter, we are going to convert the statistics application into a concurrent application that uses goroutines. However, instead of using channels, we are going to use a different approach that prevents deadlocks, while making the overall design of the program much simpler. Apart from that, there is also a version of <code>stats.go</code> named <code>statsNC.go</code> that does not create any goroutines and processes the input files sequentially.</p><p>We are only going to present the implementation of the <code>main()</code> function of <code>stats.go</code> because this is where the logic of the utility is found. However, minor additional changes exist for taking advantage of goroutines. The most time-consuming part of <code>stats.go</code> is the normalization of the time series.</p><p>What is impressive is that we converted <code>stats.go</code> into a concurrent application using a minimal amount of changes that mainly have to do with goroutine synchronization&#8212;this is a good indication of great design.</p><p>The implementation of <code>main()</code> is the following:</p><pre><code><code>func main() {
    if len(os.Args) == 1 {
        fmt.Println("Need one or more file paths!")
        return
    }
    var waitGroup sync.WaitGroup
    files = make(DFslice, len(os.Args))</code></code></pre><p>So far, we have a <code>sync.WaitGroup</code> variable for synchronizing the goroutines. Additionally, we have a slice variable named <code>files</code> that has as many elements as the length of the <code>os.Args</code> slice&#8212;<code>files[0]</code> is not going to be used.</p><p>The remaining code of <code>main()</code> is the following:</p><pre><code><code>    for i := 1; i &lt; len(os.Args); i++ {
        waitGroup.Add(1)
        go func(x int) {
            process(os.Args[x], x)
            defer waitGroup.Done()
        }(i)
    }
    waitGroup.Wait()
}</code></code></pre><p>What do we have here? There is an anonymous function that runs as a goroutine. That anonymous function requires a single parameter, which is <em><strong>the index of the command line argument that is being processed</strong></em>. There is a handy property that comes with that index: that index is unique, which means that we can use that unique index when we put data into the <code>files</code> slice&#8212;this process takes place inside <code>process()</code>. This resolves any potential race conditions as each goroutine uses a different place in <code>files</code>. Keep in mind that <code>files[0]</code> is not being used but we have decided to make <code>files</code> bigger than needed in order to put the data for the first command line argument in <code>files[1]</code>, and so on.</p><p>Apart from that, we use <code>sync</code> to wait for all goroutines to finish before exiting the program.</p><p>In order to compare <code>stats.go</code> with <code>statsNC.go</code>, we are going to use bigger datasets, which are all stored in the <code>./ch08/dataset</code> directory. The sizes of the three data files can be seen in the following output:</p><pre><code><code>$ wc dataset/*
 1518653 1518653 4119086 dataset/1.5M
 2531086 2531086 6918628 dataset/2.5M
 4049739 4049739 11037714 dataset/4.0M
 8099478 8099478 22075428 total</code></code></pre><p>A quick and dirty way to calculate the execution time of a program is using the <code>time(1)</code> UNIX utility. Using that utility, we are going to compare the execution time of <code>./ch05/stats.go</code> with <code>./ch05/stats.go</code> and see what happens:</p><pre><code><code>$ time go run stats.go ./dataset/* ./dataset/* ./dataset/*
real    0m1.240s
user    0m6.259s
sys     0m0.528s
$ time go run statsNC.go ./dataset/* ./dataset/* ./dataset/*
real    0m3.267s
user    0m7.766s
sys     0m0.535s</code></code></pre><p>What is important in the output is the values in the lines that begin with <code>real</code>. The concurrent version is about <em><strong>three times faster</strong></em> than the non-concurrent version when processing nine files. Imagine using even bigger datasets and having to process 1,000 datasets instead of just nine!</p><div><hr></div><h1>Summary</h1><p>In this important chapter, we talked about Go concurrency, goroutines, channels, the <code>select</code> keyword, shared memory, and mutexes, as well as timing out goroutines and the use of the <code>context</code> package. Bear in mind that although goroutines can process data and execute commands, they cannot communicate with each other directly but they can communicate in other ways, including channels, local sockets, and shared memory.</p><p>Remember that OS threads are controlled by the OS scheduler, whereas goroutines executed in one or more OS threads are controlled by the Go runtime. The correct terminology for when a goroutine or an OS thread is executed and then paused is <em>context-switched on and off</em>, respectively. Keep in mind that the Go scheduler checks the global queue from time to time in order to find out whether there are any goroutines waiting to be assigned to a local queue. If both the global queue and a given local queue are empty, then <em>work-stealing</em> takes place.</p><p>The main advantage of concurrency is that it allows the splitting of bigger tasks into smaller ones and the execution of each smaller task concurrently. Additionally, concurrency does a great job in distributing multiple HTTP requests among different goroutines. Lastly, concurrency makes better use of modern CPUs with multiple cores and virtual environments. However, concurrency adds complexity to the software design and the code, which affects readability and maintainability. For that reason, you might need to add concurrency last in your code, as we did with the statistics application. One other concern of concurrency is the risk of consuming all available resources making other services unreliable or even unavailable. Lastly, concurrent code is harder to benchmark&#8212;if you want to compare two concurrent implementations, it is better to compare their sequential versions that tell more about the actual algorithms and code efficiency.</p><p>What is important to remember is that the rational use of concurrency and goroutines is going to allow you to write powerful Go applications. Feel free to experiment with the concepts and the examples of this chapter to better understand goroutines, channels, and shared memory.</p><p>The next chapter is all about web services and working with the HTTP protocol in Go. Among other things, we are going to convert the statistics application into a web service.</p><div><hr></div><h1>Exercises</h1><ul><li><p>Try to implement a concurrent version of <code>wc(1)</code> that uses a buffered channel.</p></li><li><p>Try to implement a concurrent version of <code>wc(1)</code> that uses shared memory.</p></li><li><p>Try to implement a concurrent version of <code>wc(1)</code> that uses semaphores.</p></li><li><p>Try to implement a concurrent version of <code>wc(1)</code> that saves its output to a file.</p></li></ul><div><hr></div><p>To dive deeper into concurrency design, profiling techniques, generics, and Go&#8217;s evolving runtime, check out <em><strong><a href="https://www.packtpub.com/en-in/product/mastering-go-9781805127147">Mastering Go, Fourth Edition</a></strong></em> by Mihalis Tsoukalos, available from Packt. This 740-page guide is fully updated with coverage of web services, TCP/IP, REST APIs, fuzz testing, and observability, and pairs detailed explanations with real-world exercises to help you build high-performance servers, robust command-line tools, and production-grade Go systems.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.packtpub.com/en-ch/product/mastering-go-9781805122647" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VBef!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84ede4bc-d8fb-4c67-a46b-0855efef6779_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!VBef!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84ede4bc-d8fb-4c67-a46b-0855efef6779_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!VBef!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84ede4bc-d8fb-4c67-a46b-0855efef6779_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!VBef!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84ede4bc-d8fb-4c67-a46b-0855efef6779_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VBef!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84ede4bc-d8fb-4c67-a46b-0855efef6779_2250x2775" width="422" height="520.5439560439561" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/84ede4bc-d8fb-4c67-a46b-0855efef6779_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:422,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Mastering Go&quot;,&quot;title&quot;:&quot;Mastering Go&quot;,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.packtpub.com/en-ch/product/mastering-go-9781805122647&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Mastering Go" title="Mastering Go" srcset="https://substackcdn.com/image/fetch/$s_!VBef!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84ede4bc-d8fb-4c67-a46b-0855efef6779_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!VBef!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84ede4bc-d8fb-4c67-a46b-0855efef6779_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!VBef!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84ede4bc-d8fb-4c67-a46b-0855efef6779_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!VBef!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84ede4bc-d8fb-4c67-a46b-0855efef6779_2250x2775 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here is what some readers have said:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!o3Vt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b5d82c7-07a2-4fc2-9ed0-1de68158d324_1067x341.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!o3Vt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b5d82c7-07a2-4fc2-9ed0-1de68158d324_1067x341.png 424w, https://substackcdn.com/image/fetch/$s_!o3Vt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b5d82c7-07a2-4fc2-9ed0-1de68158d324_1067x341.png 848w, https://substackcdn.com/image/fetch/$s_!o3Vt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b5d82c7-07a2-4fc2-9ed0-1de68158d324_1067x341.png 1272w, https://substackcdn.com/image/fetch/$s_!o3Vt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b5d82c7-07a2-4fc2-9ed0-1de68158d324_1067x341.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!o3Vt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b5d82c7-07a2-4fc2-9ed0-1de68158d324_1067x341.png" width="1067" height="341" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6b5d82c7-07a2-4fc2-9ed0-1de68158d324_1067x341.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:341,&quot;width&quot;:1067,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:72150,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/171441031?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b5d82c7-07a2-4fc2-9ed0-1de68158d324_1067x341.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!o3Vt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b5d82c7-07a2-4fc2-9ed0-1de68158d324_1067x341.png 424w, https://substackcdn.com/image/fetch/$s_!o3Vt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b5d82c7-07a2-4fc2-9ed0-1de68158d324_1067x341.png 848w, https://substackcdn.com/image/fetch/$s_!o3Vt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b5d82c7-07a2-4fc2-9ed0-1de68158d324_1067x341.png 1272w, https://substackcdn.com/image/fetch/$s_!o3Vt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b5d82c7-07a2-4fc2-9ed0-1de68158d324_1067x341.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6xEb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64b48c91-d866-4a96-befe-521fea38005c_1073x435.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6xEb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64b48c91-d866-4a96-befe-521fea38005c_1073x435.png 424w, https://substackcdn.com/image/fetch/$s_!6xEb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64b48c91-d866-4a96-befe-521fea38005c_1073x435.png 848w, https://substackcdn.com/image/fetch/$s_!6xEb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64b48c91-d866-4a96-befe-521fea38005c_1073x435.png 1272w, https://substackcdn.com/image/fetch/$s_!6xEb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64b48c91-d866-4a96-befe-521fea38005c_1073x435.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6xEb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64b48c91-d866-4a96-befe-521fea38005c_1073x435.png" width="1073" height="435" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/64b48c91-d866-4a96-befe-521fea38005c_1073x435.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:435,&quot;width&quot;:1073,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:98847,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/171441031?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64b48c91-d866-4a96-befe-521fea38005c_1073x435.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!6xEb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64b48c91-d866-4a96-befe-521fea38005c_1073x435.png 424w, https://substackcdn.com/image/fetch/$s_!6xEb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64b48c91-d866-4a96-befe-521fea38005c_1073x435.png 848w, https://substackcdn.com/image/fetch/$s_!6xEb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64b48c91-d866-4a96-befe-521fea38005c_1073x435.png 1272w, https://substackcdn.com/image/fetch/$s_!6xEb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64b48c91-d866-4a96-befe-521fea38005c_1073x435.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f0qJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13515eea-ec51-4399-b530-4e2f69c3c44c_1072x375.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f0qJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13515eea-ec51-4399-b530-4e2f69c3c44c_1072x375.png 424w, https://substackcdn.com/image/fetch/$s_!f0qJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13515eea-ec51-4399-b530-4e2f69c3c44c_1072x375.png 848w, https://substackcdn.com/image/fetch/$s_!f0qJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13515eea-ec51-4399-b530-4e2f69c3c44c_1072x375.png 1272w, https://substackcdn.com/image/fetch/$s_!f0qJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13515eea-ec51-4399-b530-4e2f69c3c44c_1072x375.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!f0qJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13515eea-ec51-4399-b530-4e2f69c3c44c_1072x375.png" width="1072" height="375" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/13515eea-ec51-4399-b530-4e2f69c3c44c_1072x375.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:375,&quot;width&quot;:1072,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:82503,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/171441031?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13515eea-ec51-4399-b530-4e2f69c3c44c_1072x375.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!f0qJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13515eea-ec51-4399-b530-4e2f69c3c44c_1072x375.png 424w, https://substackcdn.com/image/fetch/$s_!f0qJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13515eea-ec51-4399-b530-4e2f69c3c44c_1072x375.png 848w, https://substackcdn.com/image/fetch/$s_!f0qJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13515eea-ec51-4399-b530-4e2f69c3c44c_1072x375.png 1272w, https://substackcdn.com/image/fetch/$s_!f0qJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13515eea-ec51-4399-b530-4e2f69c3c44c_1072x375.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Building a Distributed Key-Value Store in Go: From Single Node to Planet Scale]]></title><description><![CDATA[A build-to-learn exercise that walks through the architectural primitives behind Dynamo-style systems&#8212;without the abstractions.]]></description><link>https://deepengineering.substack.com/p/building-a-distributed-key-value</link><guid isPermaLink="false">https://deepengineering.substack.com/p/building-a-distributed-key-value</guid><dc:creator><![CDATA[Archit Agarwal]]></dc:creator><pubDate>Wed, 13 Aug 2025 05:59:07 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c8f1cb77-3441-4484-9b4a-463af70e5326_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most of us have either used or at least heard of systems like <strong>Redis</strong> or <strong>DynamoDB</strong>. But, have you ever wondered what kind of engineering headaches their teams had to deal with while building these giants?</p><p>In this article, we&#8217;ll go through a hands-on journey to build a <strong>distributed key-value store in Go</strong>&#8202;, &#8202;starting from a simple single-node setup and gradually scaling it conceptually to something that could power a much larger system. Along the way, we&#8217;ll uncover the trade-offs real-world systems make: sacrificing some network bandwidth to achieve eventual consistency, accepting slightly slower reads and writes (via the quorum principle) to ensure data availability, and more.</p><p>We&#8217;ll also see how a seemingly simple idea&#8202;&#8212;&#8202;<strong>hashing</strong>&#8202;&#8212;&#8202;can solve two of the biggest challenges in building massively scalable systems. To handle node failures and keep data in sync, we&#8217;ll touch on techniques such as <strong>consistent hashing</strong>, the <strong>quorum principle</strong>, <strong>vector clocks</strong>, <strong>gossip protocols</strong>, and <strong>Merkle trees</strong>.</p><p><strong>By the end of this tutorial, you will learn how to:</strong></p><blockquote><ul><li><p>Implement consistent hashing in Go to distribute (shard) data across multiple nodes</p></li><li><p>Add data redundancy to your key-value store to prevent data loss</p></li><li><p>Handle temporary network partitions and still maintain high availability using the <strong>Quorum Principle</strong></p></li><li><p>Resolve data inconsistencies caused by network partitions using <strong>Vector Clocks</strong>, and implement them in Go</p></li><li><p>Deal with permanent node failures and build a self-healing system using <strong>Gossip Protocol</strong>, <strong>Merkle Trees</strong>, and <strong>Anti-Entropy</strong> mechanisms</p></li></ul></blockquote><p>And yes, this is a <strong>build-to-learn</strong> exercise.</p><div><hr></div><h1><strong>Step 1: Building a Basic Key-Value Store</strong></h1><blockquote><p>&#8220;It all starts with a simple map&#8230; and somehow ends up running the planet.&#8221;</p></blockquote><p>In Go, a map is just a built-in dictionary that stores key-value pairs. Our journey begins with one of those &#8212; <code>map[string]string</code> &#8212; to represent a single-node, in-memory key-value store. But as we scale, this simple little map will evolve into a fault-tolerant, distributed system.</p><p>We&#8217;ll begin with the simplest possible version: an <strong>in-memory key-value store in Go</strong>. This first version uses a <code>map[string]string</code>, wrapped with methods for <strong>Put, Get, and Delete</strong>, plus TTL support so keys expire automatically. A lightweight CLI lets us interact with the store.</p><p>Here&#8217;s the core store implementation with the simplest possible foundation: an in-memory key-value store built on Go&#8217;s <code>map</code> type, with basic concurrency control and time-to-live support. This gives us the essential CRUD interface while keeping the implementation small enough to reason about:</p><pre><code><code>type Store struct {
    data map[string]item
    mu   sync.RWMutex
}

type item struct {
    value     string
    expiresAt time.Time
}

func (s *Store) Put(key, value string, ttl int) {
    s.mu.Lock()
    defer s.mu.Unlock()
    s.data[key] = item{value, time.Now().Add(time.Duration(ttl) * time.Second)}
}

func (s *Store) Get(key string) (string, error) {
    s.mu.RLock()
    defer s.mu.RUnlock()
    it, ok := s.data[key]
    if !ok || time.Now().After(it.expiresAt) {
        s.mu.RUnlock()
        s.mu.Lock()
        delete(s.data, key)
        s.mu.Unlock()
        s.mu.RLock()
        return "", errors.New("key not found or expired")
    }
    return it.value, nil
}

func (s *Store) Delete(key string) error {
    s.mu.Lock()
    defer s.mu.Unlock()

    if _, exists := s.data[key]; !exists {
        return errors.New("key not found")
    }
    delete(s.data, key)
    return nil
}</code></code></pre><p>The <code>Store</code> struct wraps a plain Go map with read/write locks to handle concurrent access. <code>Put</code> inserts a key with a TTL, <code>Get</code> retrieves it while cleaning up expired entries, and <code>Delete</code> removes it explicitly. This design works for a single-node, in-memory store, but it&#8217;s fragile&#8212;data is lost on crash, capacity is limited to one machine, and there&#8217;s no fault tolerance&#8212;making it the starting point for the distributed system we&#8217;ll build next.</p><p>You can find the full code (including the CLI) here: </p><p>&#10145; <strong><a href="https://github.com/architagr/The-Weekly-Golang-Journal/tree/main/basic-key-value-cli">GitHub: basic-key-value-cli</a></strong></p><p>Our basic key-value store is fine for experiments, but let&#8217;s be honest&#8202;&#8212;&#8202;<strong>no one would use this in production.</strong></p><p>Why? Because it has the following limitations:</p><ul><li><p><strong>Memory-only storage:</strong> If the process crashes, everything is gone.</p></li><li><p><strong>Single machine bottleneck:</strong> Limited RAM and CPU mean large datasets won&#8217;t fit, and traffic spikes can overwhelm it. What if the machine simply fails?</p></li><li><p><strong>No replication:</strong> One node means one point of failure&#8202;&#8212;&#8202;downtime equals total unavailability.</p></li><li><p><strong>Lock-based concurrency:</strong> Fine for small loads, but a bottleneck under real concurrency.</p></li></ul><blockquote><p><em><strong>In short,</strong> To build a system that survives failures, scales across multiple machines, and keeps data safe, we need to <strong>distribute</strong> it. That&#8217;s where <strong>partitioning</strong> and <strong>replication</strong> come in.</em></p></blockquote><div><hr></div><h2><strong>Step 2: Partitioning with Consistent Hashing</strong></h2><p>Once we outgrow the simplicity of a single-node key-value store, the first real challenge emerges:</p><blockquote><p><strong>How do we scale the system horizontally without breaking the rules of consistency and performance?</strong></p></blockquote><p>This is where <strong>partitioning</strong> comes in.</p><div><hr></div><h2><strong>&#127856; What is Partitioning?</strong></h2><p>Partitioning is the process of <strong>splitting your data across multiple machines</strong>, so no single node is overwhelmed.<br>You can think of it like a bakery with multiple shelves. Instead of piling all cakes onto one shelf (which could eventually collapse with the weight), we distribute them across several.</p><p>But there&#8217;s a catch.</p><p>If we just use a simple <em><strong>hash(key) %n</strong></em> approach (where <em><strong>n</strong></em> is the number of nodes), then adding/removing a node changes the result for <em>most</em> keys. That would translate to chaos in production &#8212; everything would need to be reshuffled!</p><div><hr></div><h2><strong>&#127919; Enter Consistent Hashing</strong></h2><p>Consistent hashing prevents this chaos by organising keys and nodes on a <strong>circular hash ring</strong>.</p><ul><li><p>Each node is placed on the ring based on its hash.</p></li><li><p>A key is also hashed and moves clockwise until it finds the <em>first</em> node with a greater hash value.</p></li><li><p>That node becomes the <strong>responsible owner</strong> for that key.</p></li></ul><p>To <strong>avoid uneven distribution</strong>, we don&#8217;t stop with adding a node once&#8212;we give each node multiple &#8220;avatars&#8221; on the ring using <strong>virtual nodes</strong>.</p><blockquote><p>Think of a single server showing up to the party in 10 different costumes. Why? To spread the load better and avoid creating hotspots!</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Q_je!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661cb479-a764-41bf-8a40-7a6b37f22b5f_1008x882.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Q_je!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661cb479-a764-41bf-8a40-7a6b37f22b5f_1008x882.heic 424w, https://substackcdn.com/image/fetch/$s_!Q_je!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661cb479-a764-41bf-8a40-7a6b37f22b5f_1008x882.heic 848w, https://substackcdn.com/image/fetch/$s_!Q_je!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661cb479-a764-41bf-8a40-7a6b37f22b5f_1008x882.heic 1272w, https://substackcdn.com/image/fetch/$s_!Q_je!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661cb479-a764-41bf-8a40-7a6b37f22b5f_1008x882.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Q_je!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661cb479-a764-41bf-8a40-7a6b37f22b5f_1008x882.heic" width="1008" height="882" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/661cb479-a764-41bf-8a40-7a6b37f22b5f_1008x882.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:882,&quot;width&quot;:1008,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:34547,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://architagr.substack.com/i/169915917?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661cb479-a764-41bf-8a40-7a6b37f22b5f_1008x882.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Q_je!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661cb479-a764-41bf-8a40-7a6b37f22b5f_1008x882.heic 424w, https://substackcdn.com/image/fetch/$s_!Q_je!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661cb479-a764-41bf-8a40-7a6b37f22b5f_1008x882.heic 848w, https://substackcdn.com/image/fetch/$s_!Q_je!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661cb479-a764-41bf-8a40-7a6b37f22b5f_1008x882.heic 1272w, https://substackcdn.com/image/fetch/$s_!Q_je!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661cb479-a764-41bf-8a40-7a6b37f22b5f_1008x882.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>Figure 1: Keys hashed between <strong>1&#8211;134, 291&#8211;520, and 711&#8211;800</strong> might all map to &#8220;Node 0,&#8221; and so on.</em></figcaption></figure></div><h3><strong>Minimal Go Code for Consistent Hashing with Virtual Nodes</strong></h3><p>Here&#8217;s a minimal Go implementation that shows how consistent hashing with virtual nodes works in practice&#8212;both for placing nodes on the ring and for finding which node owns a given key:</p><pre><code><code>// AddNode: places a physical node on the hash ring multiple times as virtual nodes.
func (ring *HashRing) AddNode(node ICacheNode) error {
    nodeID := node.GetIdentifier()
    for i := 0; i &lt; ring.config.VirtualNodes; i++ {
        vNodeID := fmt.Sprintf("%s_%d", nodeID, i)
        hash := ring.hash(vNodeID)
        ring.vNodeMap[hash] = node
        ring.sortedKeys = append(ring.sortedKeys, hash)
    }
    sort.Slice(ring.sortedKeys, func(i, j int) bool { return ring.sortedKeys[i] &lt; ring.sortedKeys[j] })
    return nil
}

// Get: finds which node should handle the given key.
func (ring *HashRing) Get(key string) ICacheNode {
    h := ring.hash(key)
    idx := sort.Search(len(ring.sortedKeys), func(i int) bool { return ring.sortedKeys[i] &gt;= h })
    if idx == len(ring.sortedKeys) {
        idx = 0
    }
    return ring.vNodeMap[ring.sortedKeys[idx]]
}</code></code></pre><p>The <code>AddNode</code> method ensures each physical node appears multiple times on the ring&#8212;its &#8220;virtual nodes&#8221;&#8212;to balance load. The <code>Get</code> method locates the first virtual node clockwise from the key&#8217;s hash, ensuring stable ownership. Together, they guarantee even key distribution and minimal key movement when scaling the cluster up or down.</p><blockquote><p><strong>&#9989; What We&#8217;ve Achieved So Far:</strong></p><ul><li><p>We've now <strong>partitioned our data</strong> across multiple nodes using consistent hashing.</p></li><li><p>Thanks to <strong>virtual nodes</strong>, data is <strong>evenly spread</strong>, reducing the risk of any one server becoming overloaded.</p></li><li><p>And most importantly, when nodes join or leave, only a <strong>minimal subset of keys are reassigned</strong>, keeping the system stable and predictable.</p></li></ul><p>This lays a solid foundation for building a fault-tolerant, scalable distributed key-value store.</p></blockquote><div><hr></div><h1><strong>Step 3: Ensuring Data Durability via Replication</strong></h1><p>Partitioning spreads the load but introduces a risk:</p><blockquote><p><em><strong>What if a node dies?</strong></em></p><p>Boom &#128165;&#8212; all the keys on that node are gone.</p></blockquote><p>To avoid this nightmare, we introduce <strong>replication</strong>. The idea is simple: store each key on multiple nodes.</p><p>We define a <strong>replication factor</strong> (<em><strong>N</strong></em>)&#8202;&#8212;&#8202;say 3&#8202;&#8212;&#8202;and for every key, we write it to its primary node plus <em><strong>N-1</strong></em> successors (moving clockwise on the ring). This way, if the primary node fails, one of its replicas can step in like a stunt double in an action movie. No data loss. No drama.</p><p><strong>Here&#8217;s how we fetch the responsible nodes for a given key. </strong></p><pre><code><code>func (ring *HashRing) GetNodesForKey(key string) ([]ICacheNode, error) {
    h, err := ring.generateHash(key)
    if err != nil {
        return nil, err
    }
    start := ring.search(h)
    seen := map[string]struct{}{}
    nodes := []ICacheNode{}

    for i := start; len(nodes) &lt; ring.config.ReplicationFactor &amp;&amp; i &lt; start+len(ring.sortedKeys); i++ {
        vHash := ring.sortedKeys[i%len(ring.sortedKeys)]
        node, _ := ring.vNodeMap.Load(vHash)
        n := node.(ICacheNode)
        if _, ok := seen[n.GetIdentifier()]; !ok {
            nodes = append(nodes, n)
            seen[n.GetIdentifier()] = struct{}{}
        }
    }
    return nodes, nil
}</code></code></pre><p><strong>Let&#8217;s break the preceding code down real quick:</strong></p><ul><li><p>We <strong>hash the key</strong> and locate its position on the ring. </p></li><li><p>Then we <strong>scan clockwise</strong>, collecting <strong>distinct physical nodes</strong> (deduplicating virtual nodes) until we have <code>ReplicationFactor</code> owners. This places the key on <strong>N separate nodes</strong>; as long as fewer than <strong>N</strong> of those replicas fail&#8212;and the write reached the surviving replicas&#8212;the data remains intact. </p></li><li><p>To ensure each replica is placed on a <strong>different physical node</strong>, we track the node IDs we&#8217;ve already selected in a <code>seen</code> map. This way, even if several virtual nodes for the same machine appear during the clockwise scan, only the first one counts toward the replication factor.</p></li></ul><p>Operational availability under failures, however, depends on your quorum settings (R and W), not just on replication alone.</p><p>You can find the full implementation with visuals and chaos-proof replication here: <br>&#10145; <strong><a href="https://www.linkedin.com/pulse/level-up-your-distributed-cache-go-from-basic-hash-ring-agarwal-jig9c/">Level Up Your Distributed Cache in Go</a></strong></p><div><hr></div><h1><strong>Step 4: CAP Theorem&#8202;&#8212;&#8202;The Reality Check</strong></h1><p>Replication boosts durability, but it&#8217;s not the silver bullet. It introduces a dilemma:</p><blockquote><p>What if nodes get partitioned (i.e., temporarily can&#8217;t talk to each other)?<br>Should we still serve data (Availability)?<br>Or wait until nodes reconcile (Consistency)?</p></blockquote><p>This is where the <strong>CAP theorem</strong> comes in.</p><h2><strong>Quick Refresher on CAP</strong></h2><p>In any distributed system, especially during a <strong>network partition</strong> (P) &#8212; where parts of the system lose connectivity &#8212; you can only guarantee <strong>two out of three</strong> properties:</p><ul><li><p><strong>C (Consistency)</strong>: Every read reflects the most recent write.</p></li><li><p><strong>A (Availability)</strong>: Every request receives a non-error response, even if it&#8217;s not the latest data.</p></li><li><p><strong>P (Partition Tolerance)</strong>: The system continues functioning even when network failures occur.</p></li></ul><p>You <strong>must</strong> pick your trade-offs.</p><p>Let&#8217;s look at real systems:</p><ul><li><p><strong>Etcd</strong> &#8594; CP: Prefers strict consistency. If some nodes are unreachable, it may refuse requests.</p></li><li><p><strong>DynamoDB</strong> &#8594; AP: Stays available even if replicas are temporarily inconsistent.</p></li><li><p><strong>Redis</strong> &#8594; CP-ish: Leans toward consistency, especially with persistence and replication configs.</p></li></ul><h2><strong>Our KV Store: Why AP?</strong></h2><p>Let&#8217;s say <strong>Node A</strong> gets a <em><strong>PUT user=42</strong></em>, but due to a partition, <strong>Node B</strong> hasn&#8217;t seen it yet.</p><ul><li><p>If we choose <strong>AP</strong>, we'll serve data anyway. Node B might return stale values temporarily.</p></li><li><p>If we choose <strong>CP</strong>, we'll block reads/writes until the partition is healed &#8212; painful for systems needing high uptime.</p></li></ul><p><strong>&#128161; We&#8217;re picking AP because:</strong></p><ul><li><p>It ensures high availability &#8212; requests don&#8217;t fail even if nodes are isolated.</p></li><li><p>In real-world systems (like shopping carts or feed timelines), temporary staleness is acceptable.</p></li><li><p>We can always reduce staleness with clever strategies, which brings us to&#8230;</p></li></ul><blockquote><p>&#128064; <strong>Quorums!</strong></p><p>Quorums allow us to serve reads and writes from a <em>subset</em> of nodes, while still maintaining a form of consistency. So, even in an AP system, we can be &#8220;eventually&#8221; smart.</p></blockquote><div><hr></div><h1><strong>Step 5: Making Things Consistent</strong></h1><p>Okay, so we&#8217;ve picked the AP route&#8202;&#8212;&#8202;we&#8217;re okay with availability and we&#8217;ll deal with consistency <em>eventually</em>. <strong>But </strong><em><strong>eventually</strong></em><strong> can&#8217;t mean </strong><em><strong>never</strong></em><strong>, right?</strong></p><p>We still need a way to <strong>minimize staleness</strong> and make sure clients don&#8217;t keep reading garbage forever. That&#8217;s where <strong>quorum-based reads and writes</strong> step in like that one responsible teammate who actually updates the shared document.</p><div><hr></div><h2><strong>Quorum Math 101 (No Headache, Promise)</strong></h2><p>Here&#8217;s the deal:</p><ul><li><p><strong>N</strong> = total number of replicas for a key (say 3)</p></li><li><p><strong>W</strong> = number of nodes that must <em>acknowledge</em> a write</p></li><li><p><strong>R</strong> = number of nodes to <em>respond</em> to a read</p></li></ul><p>We follow a simple but powerful rule:<br>&#128073; <strong>R + W &gt; N</strong></p><p>This ensures that any read overlaps with at least one node that has the most recent write.</p><p>Think of it like this:<br>If 2 nodes confirm a write (W = 2), and you read from 2 nodes (R = 2), you&#8217;re guaranteed at least one read hits a node that saw the latest data. Magic? Nope&#8202;&#8212;&#8202;just smart math.</p><p>To make this more concrete, here&#8217;s a minimal example showing how a write operation checks if enough replicas have acknowledged it before being considered successful:</p><pre><code><code>if ackCount &gt;= W {
    fmt.Println("Write successful")
} else {
    fmt.Println("Write failed: insufficient replicas")
}</code></code></pre><p>This check enforces the <em>W</em> part of our quorum rule. If the number of acknowledgments meets or exceeds the required write quorum, the write is accepted; otherwise, it fails, preventing partial writes from being treated as committed.</p><p>So even though we&#8217;re in an AP world, we still care about consistency&#8212;we just do it <em>selectively</em>.</p><blockquote><p>But Wait, What If Nodes Disagree? Good question.</p></blockquote><p>Let&#8217;s say a network partition happened (again) and two nodes accepted different values for the same key. Now the partition heals and it&#8217;s reunion time&#8212;but each node brings a different version to the party. Who&#8217;s right?</p><p>This is where <strong>vector clocks</strong> step in like version historians.</p><div><hr></div><h2><strong>Conflict Resolution with Vector Clocks</strong></h2><p>Let&#8217;s first define them:</p><blockquote><p>A <strong>vector clock</strong> is a mini journal that each node keeps to track how many times it has updated a key, along with updates it has <em>seen</em> from other nodes.</p></blockquote><p>Each update increments a node-specific counter, and these counters are exchanged during replication. </p><p>To represent a vector clock in code, we can use a simple mapping from node identifiers to counters, which track how many updates each node has made or observed:</p><pre><code><code>type VectorClock map[string]int
// Compare vc1 vs vc2 &#8594; newer, older, or concurrent (conflict)</code></code></pre><p>This structure gives us a compact way to compare two versions of the same key. By checking whether one clock&#8217;s counters are all greater than or equal to the other&#8217;s, we can tell if it&#8217;s strictly newer; if neither dominates, we&#8217;ve found a concurrent update&#8212;a signal that conflict resolution is needed.</p><p>For example:</p><ul><li><p>Node A might have <em><strong>{A: 2, B: 1}</strong></em></p></li><li><p>Node B might have <em><strong>{A: 1, B: 2}</strong></em></p></li></ul><p>When syncing:</p><ul><li><p>If one vector clock <em>dominates</em> the other (i.e., all its values are greater than or equal), then it's the newer version.</p></li><li><p>If <strong>neither dominates</strong> (some values are higher, others lower), the updates are <strong>concurrent</strong>&#8212;a conflict.</p></li></ul><p>What do we do then?</p><ul><li><p><strong>Last write wins?</strong> Meh.</p></li><li><p><strong>Custom merge logic?</strong> Better.</p></li><li><p><strong>Ask the user to pick?</strong> Sometimes necessary.</p></li></ul><p>This is the stuff we use to <em><strong>eventually</strong></em> reach consistency.</p><p>And just like that, our humble key-value store starts to feel like it knows what it&#8217;s doing in the face of chaos.</p><blockquote><p>Want to dive deeper into how we implemented this?<br>&#10145; <strong><a href="https://www.linkedin.com/pulse/mastering-data-consistency-quorum-principle-vector-clocks-agarwal-vt4gc">Mastering Data Consistency: Quorum Principle &amp; Vector Clocks</a></strong></p></blockquote><div><hr></div><p>Now, let&#8217;s pause for a second:</p><blockquote><p><em><strong>Have we built something production-ready?</strong><br>Not quite.</em></p></blockquote><p>Here&#8217;s a hint:<br>How do we <strong>track whether all five replicas of a key are still alive?</strong><br>What if <strong>two machines go down</strong>&#8202;? How do we know, and how do we restore their missing data to new nodes?</p><p>And that&#8217;s the beauty of system design:<br>There&#8217;s <strong>no single magic concept</strong> that fixes everything. Each solution comes with trade-offs, which introduce new challenges. That&#8217;s what makes this field exciting&#8202;&#8212;&#8202;and endless.</p><p>And that is why, in the next sections, we&#8217;ll dive into <strong>failure detection and recovery</strong>:</p><ul><li><p>How to detect dead nodes (<strong>Gossip protocol</strong>).</p></li><li><p>How to repair inconsistent replicas (<strong>Merkle trees</strong>).</p></li><li><p>How to handle temporary vs. permanent failures gracefully.</p></li></ul><p>Let&#8217;s get started.</p><div><hr></div><h1><strong>Step 6: Dealing with Failures</strong></h1><blockquote><p><em>&#8220;Because stuff will break. It always does.&#8221;</em></p></blockquote><p>A truly distributed system isn&#8217;t judged by how well it runs when everything&#8217;s perfect &#8212; it&#8217;s judged by how gracefully it recovers when things go sideways.</p><p>Our key-value store may look solid now, but sooner or later, the universe throws a wrench: machines crash, networks glitch, and nodes vanish without even saying goodbye.</p><h4><strong>So, how do we stay resilient?</strong></h4><p>Let&#8217;s break this down into two categories of failures &#8212; temporary ones we can ride out, and permanent ones we need to actively repair.</p><div><hr></div><h2><strong>1. Temporary Failures: Don&#8217;t Panic Yet</strong></h2><p>Sometimes a node isn&#8217;t dead&#8202;&#8212;&#8202;it&#8217;s just slow or its network is acting up. We can handle these &#8220;soft&#8221; failures using:</p><blockquote><p><strong>Retries with Exponential Backoff</strong><br>Instead of hammering the same request repeatedly, back off a bit each time.</p></blockquote><p>To tolerate transient link or node hiccups, we retry the write with exponential backoff&#8212;waiting longer after each failed attempt to avoid amplifying congestion:</p><pre><code><code>for attempt := 1; attempt &lt;= maxRetries; attempt++ {
    err := sendToNode(node, data)
    if err == nil { break }
    time.Sleep(time.Duration(math.Pow(2, float64(attempt))) * time.Millisecond)
}</code></code></pre><p>This loop stops on the first success and otherwise doubles the sleep each time, reducing pressure on a slow or flaky path. In production you&#8217;d typically add jitter and a max backoff, honor context cancellation/timeouts, and retry only on errors you&#8217;ve classified as transient&#8212;reserving hinted handoff for cases where the target remains unreachable.</p><p>&#128161; This pattern lets us tolerate transient failures without nuking the node or overloading the network. It&#8217;s like giving the system a breather before asking again.</p><blockquote><p><strong>Hinted Handoff</strong><br>When a node is temporarily unreachable, we don&#8217;t just drop the write. Instead, another healthy node steps up and <strong>holds the data &#8220;on behalf&#8221;</strong> of the sick node &#8212; a feature known as <em>hinted handoff</em>. Once the target node is back online, the hint is delivered.</p><p>It&#8217;s like asking a coworker to take notes for you in a meeting you&#8217;re late to. The data stays safe, and the system keeps humming.</p></blockquote><div><hr></div><h2><strong>2. Permanent Failures: Time to Repair</strong></h2><p>Now for the real disasters &#8212; disks fry, servers die, or someone yanks the wrong power cable.</p><p>&#129504; <strong>First step? Detect the failure quickly.</strong></p><h3><strong>&#128483;&#65039; Gossip Protocol (a.k.a. Distributed Rumor Mill)</strong></h3><p>The Gossing Protocol is a neat trick inspired by how gossip spreads in an office.</p><p>To detect failures without a central coordinator, each node periodically &#8220;gossips&#8221; a heartbeat to a randomly chosen peer. Over time, this epidemic exchange spreads liveness information across the cluster:</p><pre><code><code>func gossipHealth(nodeList []Node) {
    peer := pickRandomNode(nodeList)
    _ = sendHeartbeat(peer)
}</code></code></pre><p>This toy loop shows the core idea&#8212;random peer selection and a heartbeat send&#8212;but real gossip runs on a timer with jitter, tracks per-member state (incarnation/version), and uses suspicion/timeout thresholds (e.g., &#966;-accrual) before declaring a node dead. Implementations typically piggyback membership deltas on heartbeats, use push/pull anti-entropy, and limit fan-out to keep bandwidth bounded while still achieving fast, probabilistic dissemination.</p><p>&#128073; This heartbeat-sharing helps identify dead nodes <em>without a central coordinator</em>. It&#8217;s fault-tolerant, fast, and wonderfully gossipy.</p><p>&#128218; <strong>To see how the Gossip Protocol is implemented in Go, Read:<br></strong>&#10145; <a href="https://www.linkedin.com/pulse/how-implement-gossip-protocol-distributed-systems-using-agarwal-lfnwc">How to Implement Gossip Protocol in Go</a></p><div><hr></div><h2><strong>When Nodes Come Back: Data Syncing</strong></h2><p>Okay, so a node is back from the dead (or replaced). Now we need to <strong>catch up</strong> with all the data it missed. But syncing <em>everything</em> between nodes is too expensive, and resolving each key using vector clocks is overkill.</p><p>So, how do we <em>efficiently</em> find the differences? The answer is <strong>Merkle Trees</strong></p><p><em>Merkle Trees are not just for blockchains, folks.</em></p><h4><strong>&#127795; What is a Merkle Tree?</strong></h4><blockquote><p>A <strong>Merkle Tree</strong> is a tree of hashes. Each leaf node represents a hashed key-value pair. Parent nodes hash their children&#8217;s values. Eventually, the root hash represents the entire dataset.</p></blockquote><p>This structure lets us compare <strong>large sets of data</strong> by comparing just a few hashes.</p><p>To resync a returning node without comparing every key, we build a Merkle tree: a deterministic tree of hashes over a <em>sorted</em> key range. Leaves typically hash a key&#8217;s identity and version (or value hash), parents hash the concatenation of their children, and the root serves as a compact digest of the whole range. Two replicas can compare roots first and only drill into subtrees that differ:</p><pre><code><code>type MerkleNode struct {
    hash  []byte
    left  *MerkleNode
    right *MerkleNode
}
// Compare root hashes: if they differ, traverse down to find mismatched ranges.</code></code></pre><p>Here&#8217;s how the code works:</p><p>Start by comparing the <strong>root hashes</strong> of two replicas.</p><ul><li><p>If they match: &#9989; All good.</p></li><li><p>If they differ: &#9940; Traverse down the tree.</p></li><li><p>Eventually, you pinpoint <em>exactly</em> which ranges differ &#8212; and sync only those.</p></li></ul><p>&#129504; Think of it as doing a diff between directories, but at scale and optimized with hashes.</p><p>In production you&#8217;ll want: </p><ol><li><p>Canonical key ordering and fixed range partitioning so trees are comparable, </p></li><li><p>Leaves that hash something like <code>H(key || version || valueHash)</code> to handle deletes/TTL via tombstones, </p></li><li><p>A stable cryptographic hash (e.g., SHA-256), </p></li><li><p>Incremental rebuilds or rolling windows so the tree stays fresh without full recompute, and </p></li><li><p>Rate-limited, chunked transfer of the differing ranges. This turns &#8220;catch-up after outage&#8221; into a targeted, bounded anti-entropy pass rather than a full dataset diff.</p></li></ol><div><hr></div><p><strong>So Why Are Merkle Trees Important?</strong></p><p>Earlier, we mentioned hashing helps solve two hard scaling problems:</p><ul><li><p><strong>Distributing data across nodes</strong> &#8594; we used <em>consistent hashing</em>.</p></li><li><p><strong>Reconciling data between replicas</strong> &#8594; <em>Merkle Trees</em> to the rescue.</p></li></ul><p>Merkle Trees aren&#8217;t <em>better</em> than consistent hashing &#8212; they&#8217;re <em>complementary</em>. One spreads the load, the other keeps the data in sync.</p><div><hr></div><p><strong>Benefits of Merkle Trees in Repair</strong></p><ul><li><p><strong>Space efficient</strong>: O(n)</p></li><li><p><strong>Fast to detect differences</strong>: O(log n)</p></li><li><p><strong>Efficient syncs</strong>: Transfer only what&#8217;s necessary</p></li></ul><p>That&#8217;s why systems like <strong>Cassandra</strong> use them for anti-entropy repairs, and <strong>Bitcoin</strong> uses them for transaction validation.<br><br>&#128218; <strong>To see how Merkle Trees power scalable repair, read:</strong><br>&#10145; <a href="https://www.linkedin.com/pulse/merkle-trees-anti-entropy-concepts-implementation-archit-agarwal-ucjlc">How Merkle Trees Power Scalable Repair in Distributed Systems</a><em>.</em></p><p>With that, our system is no longer just functional &#8212; it&#8217;s resilient.</p><p>Failures? Let them come.<br>We&#8217;ve got retries, handoffs, gossip, and trees on our side.</p><h1><strong>Step 7: Final Design Summary</strong></h1><p>We&#8217;ve come a long way from a simple <code>map[string]string</code>. Let&#8217;s connect the dots:</p><ul><li><p><strong>Hash Ring &#8594; Partitioning: </strong>Keys are evenly distributed across multiple nodes using consistent hashing (and virtual nodes), avoiding hotspots and enabling horizontal scaling.</p></li><li><p><strong>Replication &#8594; Fault Tolerance: </strong>Each key is stored on multiple nodes. If one node fails, replicas ensure both the data and the system keep running.</p></li><li><p><strong>Quorum &#8594; Consistency Control: </strong>By using quorum reads/writes (R + W &gt; N), we avoid waiting for all replicas while still preventing our AP system from drifting too far into inconsistency.</p></li><li><p><strong>Gossip &#8594; Failure Detection: </strong>Nodes exchange lightweight health updates, quickly spotting which peers are alive, slow, or permanently gone.</p></li><li><p><strong>Merkle Trees &#8594; Repair: </strong>When failed nodes recover or new nodes join, Merkle Trees efficiently detect and sync only the out-of-date key ranges&#8202;&#8212;&#8202;no need to resend the entire dataset.</p></li></ul><p>Here&#8217;s a high-level view of the final architecture:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!B_E1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2101a18f-a112-4182-8b25-2ec7b061676b_1242x701.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B_E1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2101a18f-a112-4182-8b25-2ec7b061676b_1242x701.heic 424w, https://substackcdn.com/image/fetch/$s_!B_E1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2101a18f-a112-4182-8b25-2ec7b061676b_1242x701.heic 848w, https://substackcdn.com/image/fetch/$s_!B_E1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2101a18f-a112-4182-8b25-2ec7b061676b_1242x701.heic 1272w, https://substackcdn.com/image/fetch/$s_!B_E1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2101a18f-a112-4182-8b25-2ec7b061676b_1242x701.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B_E1!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2101a18f-a112-4182-8b25-2ec7b061676b_1242x701.heic" width="1200" height="677.2946859903382" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2101a18f-a112-4182-8b25-2ec7b061676b_1242x701.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:701,&quot;width&quot;:1242,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:30185,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://architagr.substack.com/i/169915917?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2101a18f-a112-4182-8b25-2ec7b061676b_1242x701.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!B_E1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2101a18f-a112-4182-8b25-2ec7b061676b_1242x701.heic 424w, https://substackcdn.com/image/fetch/$s_!B_E1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2101a18f-a112-4182-8b25-2ec7b061676b_1242x701.heic 848w, https://substackcdn.com/image/fetch/$s_!B_E1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2101a18f-a112-4182-8b25-2ec7b061676b_1242x701.heic 1272w, https://substackcdn.com/image/fetch/$s_!B_E1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2101a18f-a112-4182-8b25-2ec7b061676b_1242x701.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figure 2: Final Architecture</figcaption></figure></div><p><strong>Key Responsibilities per Node:</strong></p><ul><li><p>Handle CRUD operations for its key range</p></li><li><p>Detect failures (via gossip)</p></li><li><p>Resolve version conflicts (vector clocks)</p></li><li><p>Repair missing data (Merkle Trees)</p></li><li><p>Maintain replication sets</p></li><li><p>Store and serve data</p></li></ul><p>A small in-memory map has now evolved into a resilient, horizontally scalable distributed key-value store.</p><div><hr></div><h3><strong>Conclusion</strong></h3><p>From a single in-memory map to a cluster powered by hashing, gossip, quorums, and Merkle Trees&#8202;&#8212;&#8202;you&#8217;ve just walked through the anatomy of a distributed key-value store.</p><p>This was a <strong>build-to-learn</strong> journey, not a production-ready blueprint, but the core ideas are the same ones that power DynamoDB, Cassandra, and other real-world giants.</p><p>To explore the detailed Go implementation, visuals, and deeper theory, check out the following GitHub repos:</p><p><strong><a href="https://github.com/architagr/The-Weekly-Golang-Journal/tree/main/basic-key-value-cli">The-Weekly-Golang-Journal/basic-key-value-cli</a></strong></p><p><strong><a href="https://github.com/architagr/The-Weekly-Golang-Journal/tree/main/vector_clock">Implementation of Vector Clock</a></strong></p><p><strong><a href="https://github.com/architagr/The-Weekly-Golang-Journal/tree/main/gossip-protocol">Implementation of Gossip protocol in Golang</a></strong></p><div><hr></div><p><strong>About the Author:</strong></p><p><strong>Archit Agarwal</strong> is a <strong>Principal Member of Technical Staff at Oracle</strong>, where he engineers ultra-low latency authorization services in Golang. He is also the creator of <strong><a href="https://www.linkedin.com/newsletters/the-weekly-golang-journal-7261403856079597568/">The Weekly Golang Journal</a></strong> (&#8220;Mastering Golang, one issue at a time&#8221;), dedicated to translating advanced system design concepts into practical tools for engineers.</p><p>Archit&#8217;s focus is on elevating system performance and operational efficiency&#8212;whether by harnessing deep knowledge of programming language internals, optimizing SQL queries and backend logic for speed, or using modern DevOps practices to drive down infrastructure costs and boost developer productivity. </p><p>He actively explores and refines ways to make distributed systems not just theoretically robust, but highly efficient in production&#8212;emphasizing practical optimizations, smarter use of CI/CD pipelines, and leveraging cloud-native solutions for both scalability and cost-effectiveness.</p><p>If you enjoyed this deep dive and want more such content on distributed systems and Go, you can follow and connect with Archit Agarwal on the following platforms:</p><ul><li><p>&#128236;<strong>Newsletter</strong>: <a href="https://www.linkedin.com/newsletters/the-weekly-golang-journal-7261403856079597568/">The Weekly Golang Journal</a><br>Bite-sized, practical reads on Go, backend engineering, and system design every Wednesday.</p></li><li><p>&#128188;<strong>LinkedIn</strong>: <a href="https://www.linkedin.com/in/architagarwal984/">Archit Agarwal</a><br>I post on Go, distributed systems, and dev life stories.</p></li><li><p>&#9997;&#65039; <strong>Medium</strong> &#8211; <a href="https://medium.com/@architagr">@architagr</a><br>More long-form reads and behind-the-scenes of my projects.</p></li><li><p>&#128038; <strong>Twitter (X)</strong> &#8211; <a href="https://x.com/architagr">@architagr</a><br>Quick thoughts, code snippets, and my dev brain at 2 AM.</p></li></ul><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://deepengineering.substack.com/p/building-a-distributed-key-value?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://deepengineering.substack.com/p/building-a-distributed-key-value?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://deepengineering.substack.com/p/building-a-distributed-key-value/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://deepengineering.substack.com/p/building-a-distributed-key-value/comments"><span>Leave a comment</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[TableGen – LLVM Swiss Army Knife for Modeling]]></title><description><![CDATA[The complete &#8220;Chapter 6: TableGen &#8211; LLVM Swiss Army Knife for Modeling&#8221; from the book LLVM Code Generation by Quentin Colombet (Packt, May 2025).]]></description><link>https://deepengineering.substack.com/p/tablegen-llvm-swiss-army-knife-for</link><guid isPermaLink="false">https://deepengineering.substack.com/p/tablegen-llvm-swiss-army-knife-for</guid><dc:creator><![CDATA[Quentin Colombet]]></dc:creator><pubDate>Thu, 31 Jul 2025 09:02:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!VMSy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba154a1-1adc-42aa-9613-d830b86b54fc_2250x2775" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For every target, there are a lot of things to model in a compiler infrastructure to be able to do the following:</p><ul><li><p>Represent all the available resources</p></li><li><p>Extract all the possible performance</p></li><li><p>Manipulate the actual instructions</p></li></ul><p>This list is not exhaustive, but the point IS that you need to model a lot of details of a target in a compiler infrastructure.</p><p>While it is possible to implement everything with your regular programming language, such as C++, you can find more productive ways to do so. In the LLVM infrastructure, this takes the form of a <strong>domain-specific language</strong> (<strong>DSL</strong>) called TableGen.</p><p>In this chapter, you will learn the TableGen syntax and how to work your way through the errors reported by the TableGen tooling. These skills will help you be more productive when working with this part of the LLVM ecosystem.</p><p>This chapter focuses on TableGen itself, not the uses of its output through the LLVM infrastructure. How the TableGen output is used is, as you will discover, TableGen-backend-specific and will be covered in the relevant chapters. Here, we will use one TableGen backend to get you accustomed to the structure of the TableGen output, starting you off on the right foot for the upcoming chapters.</p><p>Before getting started with TableGen, let&#8217;s briefly discuss the technical requirements.</p><div><hr></div><h1>Technical requirements</h1><p>In this chapter, you will wrestle directly with the TableGen tooling that comes with the LLVM releases. At this point, you know the drill concerning the tools that you need to build a project using the LLVM infrastructure (Git, CMake, Ninja, and the C++ toolchain).</p><p>Additionally, you will find the code for the examples in this chapter in a folder named <code>ch6</code>, which can be found in the GitHub repository of this book: <a href="https://github.com/PacktPublishing/LLVM-Code-Generation">https://github.com/PacktPublishing/LLVM-Code-Generation</a>.</p><p>Without further ado, let&#8217;s start our journey with TableGen.</p><div><hr></div><h1>Getting started with TableGen</h1><p>The name <strong>TableGen</strong> stems from its original usage &#8211; generating tables. For instance, TableGen generates the table that represents all the registers of a target. TableGen outgrew this purpose and is now used to model a wide range of things, from Clang&#8217;s command-line options to <strong>multi-level intermediate representation</strong> (<strong>MLIR</strong>) operations&#8217; boilerplate C++ code, or used directly within LLVM to generate the instruction selection tables, and so on.</p><p>Fundamentally, TableGen is a DSL to produce records. A <strong>record</strong> is an entity with a name and an arbitrary number of fields, where each field has its own type.</p><p>How these records are used and what output TableGen generates from them depends on the specific TableGen backend.</p><p>We will survey one of the TableGen backends used in this book in the <em>Discovering a TableGen backend</em> section, but you will learn how to use this backend and the other ones in the relevant upcoming chapters.</p><p>TableGen&#8217;s strength lies in how you can structure the generation of your records such that you can factor out the repeated parts of records.</p><p>For instance, imagine that you want to produce records that hold the ages and names of people. Without even describing the TableGen syntax yet, this could look like the following snippet:</p><pre><code>class Person&lt;int age, string name&gt; {
  int _age = age;
  string _name = name;
}
def A: Person&lt;23, "A"&gt;;
def B: Person&lt;64, "B"&gt;;
def /*Anonym*/: Person&lt;43, "anonymous"&gt;;</code></pre><p>Note how the boilerplate of our records is gathered in just one location, the <code>Person</code> class, and how easy it is to create a record for each person (<code>A</code>, <code>B</code>, etc.).</p><p>Then, you can process that input (saved in a file named <code>person.td</code>, in this case) through TableGen with the following command:</p><pre><code>$ ${LLVM_INSTALL_DIR}/bin/llvm-tblgen person.td</code></pre><p>Running this command will yield the following records:</p><pre><code>def A {&#9;// Person
  int _age = 23;
  string _name = "A";
}
def B {&#9;// Person
  int _age = 64;
  string _name = "B";
}
def anonymous_0 {&#9;// Person
  int _age = 43;
  string _name = "anonymous";
}</code></pre><p>As you can see, TableGen&#8217;s basic functionality expands a structured representation of your records into a mostly flat representation. The parts that do not get flattened are the fields with non-built-in types.</p><p>To summarize, TableGen is a sort of glorified string concatenation tool, at least for the frontend part.</p><p>The interesting bits happen when you enable a TableGen backend (through one of the <code>--gen-xxx</code> options of the <code>llvm-tblgen</code> tool). When a TableGen backend is enabled, TableGen feeds the records, after flattening them all, to the related backend. The backend then generates what is expected from these records, and this content is included in the related part of the LLVM infrastructure. Where and how things are included depends on the usage of the generated information, but the general mechanism remains the same for all of them, as illustrated in <em>Figure 6.1</em>.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!StYc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c8620f-766f-4caf-a0c1-e3092c8f3f4e_866x178.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!StYc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c8620f-766f-4caf-a0c1-e3092c8f3f4e_866x178.png 424w, https://substackcdn.com/image/fetch/$s_!StYc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c8620f-766f-4caf-a0c1-e3092c8f3f4e_866x178.png 848w, https://substackcdn.com/image/fetch/$s_!StYc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c8620f-766f-4caf-a0c1-e3092c8f3f4e_866x178.png 1272w, https://substackcdn.com/image/fetch/$s_!StYc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c8620f-766f-4caf-a0c1-e3092c8f3f4e_866x178.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!StYc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c8620f-766f-4caf-a0c1-e3092c8f3f4e_866x178.png" width="866" height="178" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17c8620f-766f-4caf-a0c1-e3092c8f3f4e_866x178.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:178,&quot;width&quot;:866,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Figure 6.1: Usage of TableGen in LLVM&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figure 6.1: Usage of TableGen in LLVM" title="Figure 6.1: Usage of TableGen in LLVM" srcset="https://substackcdn.com/image/fetch/$s_!StYc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c8620f-766f-4caf-a0c1-e3092c8f3f4e_866x178.png 424w, https://substackcdn.com/image/fetch/$s_!StYc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c8620f-766f-4caf-a0c1-e3092c8f3f4e_866x178.png 848w, https://substackcdn.com/image/fetch/$s_!StYc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c8620f-766f-4caf-a0c1-e3092c8f3f4e_866x178.png 1272w, https://substackcdn.com/image/fetch/$s_!StYc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c8620f-766f-4caf-a0c1-e3092c8f3f4e_866x178.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 6.1: Usage of TableGen in LLVM</em></p><p>In <em>Figure 6.1</em>, an input file, usually with the <code>.td</code> suffix, which stands for <strong>target description</strong>, is fed to TableGen. The TableGen frontend generates flattened records and feds them to the desired backend. The backend generates its output in a file with a <code>.inc</code> suffix. That suffix indicates that this file needs to be included somewhere else to make sense. The type of output in the <code>.inc</code> file depends on the backend, but usually in LLVM, TableGen is used to produce C++ code.</p><p>The invocation of TableGen happens at <strong>build time</strong>. The content of certain LLVM files depends on the presence of the <code>.inc</code> files, and they are generated as part of the build process. This is why building TableGen in <strong>release</strong> mode, as described in , can help you speed up your build time even if you are interested in building your whole compiler in <strong>debug</strong> mode. </p><blockquote><p><strong>Note</strong></p><p>Even if you are tempted to modify the content of a <code>.inc</code> file, do not! Although this is fine for exploration purposes, the <code>.inc</code> suffix is used as an indication that the related file has been automatically generated. In other words, if you want to modify it, you must modify its source; hence, find its related <code>.td</code> file.</p></blockquote><p>In this section, you learned the basic mechanics of TableGen. You saw that it is used as a DSL to generate, at build time, information included by the rest of the LLVM infrastructure. You also learned that what is generated is TableGen-backend specific.</p><p>Before looking at the TableGen backends more closely, let&#8217;s get a primer on the TableGen syntax.</p><div><hr></div><h1>The TableGen programming language</h1><p>In this section, we will offer a primer on the TableGen syntax. The goal here is not for you to become a TableGen programmer but to know enough so that you can understand what you read and write when working with TableGen in the rest of this book. For a more thorough explanation of the TableGen syntax, consult the <em>Further reading</em> section.</p><p>As you saw in the previous section, TableGen is a language used to generate records. It relies on two main constructs to describe its records:</p><ul><li><p><code>class</code>: A way to structure, pre-fill, and specify a type for records</p></li><li><p><code>def</code>: An instantiation of a record</p></li></ul><p>The general syntax to describe a record is as follows:</p><pre><code>def [optionalName][: optionalClassA[&lt;argN[, argM]*&gt;][, optionalClassB[&lt;argV[, argW]*&gt;]]*] [{
  [type fieldName [ = value];]*
}];</code></pre><p>In this snippet, everything between <code>[</code> and <code>]</code> is optional, and the <code>*</code> character means, as usual for regular expression, that the previous expression is repeated zero or more times.</p><p>Additionally, <code>argX</code> are the potentially required arguments of the related class. They must match in type and number at least all the non-default arguments specified for this class (see the <em>Types</em> section for more details). Then, <code>value</code> is used to initialize <code>fieldName</code>, and it must have the same type as <code>type</code>. The syntax of <code>value</code> depends on <code>type</code>.</p><blockquote><p><strong>Note</strong></p><p>When you use other records in your fields (e.g., when <code>value</code> is another record), TableGen creates a reference to the original record. In other words, records are never copied.</p></blockquote><p>A record instantiated with <code>optionalClassX</code> inherits all the fields of the respective class or classes. You can assign as many classes as you want to a record, as shown in the following snippet:</p><pre><code>def MyRecord: classA, classB;</code></pre><p>The resulting record will have the following traits:</p><ul><li><p>Be usable as an instance of each of these classes (for example, <code>MyRecord</code> can be used in both <code>list&lt;classA&gt;</code> and <code>list&lt;classB&gt;</code> instances).</p></li><li><p>Be the union of all the fields of all the classes.</p></li></ul><blockquote><p><strong>Note</strong></p><p>If two classes have a field with the same name and are used together to instantiate a record, only the last seen field is kept. Similarly, if a record declares a field of the same name, only this one is kept. In other words, unlike C++ inheritance, name collisions cannot be disambiguated, and the last instantiated one wins.</p></blockquote><p>Note that a record does not need to be typed. In other words, a record can be instantiated with just a <code>def</code> statement.</p><p>Let&#8217;s now focus on the available types in TableGen, the topic of our next section.</p><h2>Types</h2><p>While a record may not be typed, every field in a record must be typed. The types can be either one of the built-in types or user-defined.</p><p>The built-in types are as follows:</p><ul><li><p><code>int</code>: A 64-bit integer value</p></li><li><p><code>bit</code>: A zero or one</p></li><li><p><code>bits&lt;size&gt;</code>: A sequence of <code>size</code> of zeros and ones</p></li><li><p><code>string</code>: A sequence of arbitrary characters</p></li><li><p><code>list&lt;type&gt;</code>: A sequence of <code>type</code> elements</p></li><li><p><code>dag</code>: A structure that represents a <strong>directed acyclic graph</strong> (<strong>DAG</strong>). Without going into details, you can view this as arbitrary nested lists where each list starts with an operator (e.g., <code>(add (mul a, b), (div c, d))</code> is the DAG that represents a math expression of the form <code>(a * b) + (c / d)</code>). This is useful, for instance, to describe instruction selection patterns.</p></li></ul><p>To initialize the integer-like types (<code>int</code>, <code>bit</code>, and <code>bits</code>) you can use your preferred <em>C-style</em> syntax for decimal and hexadecimal numbers, or the TableGen binary format in the <code>0b</code> form, followed by zeros and ones.</p><p>For strings, simply use the <em>C-style</em> string syntax.</p><p>On top of the built-in types, you can create your own types using the <code>class</code> keyword. A class offers a convenient way to structure your records and can be parametrized with optional arguments, using a syntax close to C++ templates.</p><p>Using the same convention as the definition of a record, the definition of a class resembles the following:</p><pre><code>class ClassName[&lt;type arg1[, type arg2]*&gt;] [: superclass1[&lt;argN[, argM]*&gt;] [, superclass2[&lt;argV[, argW]*&gt;]]*] [{
  [type fieldName [ = value];]*
}];</code></pre><p>The class&#8217;s arguments can be set to a default value and used in the body of the class.</p><p>For instance, we can modify our <code>Person</code> class from earlier to set a default name:</p><pre><code>class Person&lt;int age, string name = "anonymous"&gt; {
  int _age = age;
  string _name = name;
}</code></pre><p>With this snippet, the instantiation of a record of type <code>Person</code> does not need to set the <code>name</code> argument anymore. Instead, <code>"anonymous"</code> will be automatically filled in.</p><p>This concludes the primer on types in TableGen.</p><p>Next, let&#8217;s introduce some of the programming capabilities that TableGen offers.</p><h2>Programming with TableGen</h2><p>TableGen offers some basic programmability features to make the description of your records more compact. These are called bang operators, and they are all prefixed with the <code>!</code> character (i.e., the bang operator in C, hence the name) &#8211; for example, <code>!add</code> and <code>!range</code>.</p><p>For instance, you can define a record with a list of integers from <code>0</code> to <code>10</code> without the number <code>5</code>, and then count how many items there are in the list by using something like the following:</p><pre><code>def MyRecord {
  list&lt;int&gt; ZeroTo10No5 =
      !filter(var, !range(0, 10), !ne(var, 5));
  int num = !size(ZeroTo10No5);
}</code></pre><p>In this snippet, we define a list of integers from <code>0</code> to <code>10</code> by using the <code>!range</code> operator. We then give this list of integers to the <code>!filter</code> operator. This operator iterates through this list and checks whether the current element, stored in the variable named <code>var</code> (defined by the first argument of the operator), matches the predicate given as its third argument &#8211; in this case, the <code>!ne</code> operator. <code>!ne</code> checks that two values are not equal.</p><p>Note how you can reference other fields (here <code>ZeroTo10No5</code>) when defining another one (here, <code>num</code>).</p><p>You can find the complete list of the bang operators at <a href="https://llvm.org/docs/TableGen/ProgRef.html#bang-operators.">https://llvm.org/docs/TableGen/ProgRef.html#bang-operators.</a></p><p>Before we finish with the TableGen syntax, there is one last important concept that you need to be aware of &#8211; multi-class. Let&#8217;s cover this topic in our next section.</p><h2>Defining multiple records at once</h2><p>With its obsession with compact code, TableGen offers a way to create a template for multiple records at once. You can use it with the <code>multiclass</code> keyword for the description of the template and the <code>defm</code> keyword for the instantiation of the records.</p><p>Before we dive into the syntax of these constructs, we want to highlight that, unlike classes, multi-classes are not types per se. You can instantiate a (multi-)record with a multi-class, but you cannot use a multi-class as the type parameter of <code>list</code>, for instance. We will illustrate this difference with an example, but first, let&#8217;s lay down the syntax.</p><p>Using the same convention as before, here is what a multi-class definition looks like:</p><pre><code>multiclass name[&lt;type [arg1, type arg2]*&gt;][: optionalMulticlassA[&lt;argN[, argM]*&gt;][, optionalMulticlassB[&lt;argG[, argH]*&gt;]]* [{
  [def|defm recordSuffix  // full record definition]*
}]</code></pre><p>In this snippet, you can see that you can only describe multi-classes through other multi-classes (i.e., the initialization list accepts only multi-classes), but you can put as many (multi-)records as you want in its body.</p><p>While the (multi-)record&#8217;s definition in the multi-class body is the full record&#8217;s body (i.e., it follows the syntax shown earlier), the record is not instantiated just yet. Instead, the records described in a multi-class are instantiated when a standalone <code>defm</code> statement is used.</p><p>Using the same convention as before, a <code>defm</code> statement looks like the following expression:</p><pre><code>defm [optionalName] : multiclassA[&lt;argN[, argM]*&gt;][, multiclassB[&lt;argV[, argW]*&gt;]]*[,classD[&lt;argY[, argZ]*&gt;]]*;</code></pre><p>A <code>defm</code> statement instantiates multiple records at once. All the records from all the <code>multiclassX</code> templates are instantiated, and their names are, by default, prepended by <code>optionalName</code>. In other words, by using this snippet and the previous multi-class one, the resulting record&#8217;s name will be <code>optionalName</code> followed by <code>recordSuffix</code>. Additionally, each record is augmented with the fields found in the union of all of <code>classX</code>.</p><blockquote><p><strong>Note</strong></p><p>If you do not set a name for your record (in other words, if you leave a blank between the <code>def</code> or <code>defm</code> keyword and the body of the (multi-)record), TableGen will automatically assign a unique name to your record(s). You have actually already experienced that, in the very first example, where <code>def /*Anonym*/: ...</code> produced the record named <code>def anonymous_0</code>!</p></blockquote><p>To make the <code>multiclass</code> and <code>defm</code> constructs more concrete, let&#8217;s look at an example that uses them.</p><p>Consider the following snippet:</p><pre><code>multiclass Bundle&lt;string base&gt; {
  def A {
    string name = !strconcat(base, "-", "A");
    int price = 12;
    int weight = 1;
  }
  def B {
    string name = !strconcat(base, "-", "B");
    string tag = "special";
  }
}
class ShippingPrice&lt;int arg&gt; {
  int shippingPrice = arg;
}
defm valuedBundle : Bundle&lt;"valued"&gt;, ShippingPrice&lt;5&gt;;</code></pre><p>In this snippet, we declare one multi-class (<code>Bundle</code>) and one class (<code>ShippingPrice</code>). The multi-class defines two records, <code>A</code> and <code>B</code>. When we instantiate this multi-class with <code>valuedBundle</code>, we immediately create two records, named <code>valuedBundleA</code> and <code>valuedBundleB</code> (i.e., the name given to the <code>defm</code> statement, prepended to the two definitions from within the multi-class). Moreover, these records end up with a <code>shippingPrice</code> field with a value of <code>5</code>, due to the usage of <code>ShippingPrice&lt;5&gt;</code> at the end of the <code>defm</code> statement.</p><p>Note that you can see which records are generated for this example by following the <code>README.md</code> instructions in the <code>ch6</code> file for the <code>multiclass.td</code> example.</p><p>Now that you have a better grasp of what a multi-class is, you can understand why this is not the kind of type that can appear in a <code>list</code> definition. To clarify this, as you saw, a multi-class is a collection of records, not a type per se. Each of these records may have distinct types, and they are technically never held together.</p><p>In other words, the following snippet is invalid:</p><pre><code>list&lt;Bundle&gt; // error</code></pre><p>However, let&#8217;s say we add a definition of type <code>Gift</code> under the name <code>C</code> in our <code>Bundle</code> multi-class. After the <code>defm</code> statement of our previous example, a <code>valuedBundleC</code> record exists and has the <code>Gift</code> type (defined earlier as a class). Now, this record can be used in a list of <code>Gift</code>:</p><pre><code>def AnotherRecord {
  list&lt;Gift&gt; gifts = [valuedBundleC];
}</code></pre><p>We are almost done with our survey of the TableGen programming language; we are missing just one thing relating to field assignment that is used a lot in the LLVM code base. Let&#8217;s address that in the next section.</p><h2>Assigning fields</h2><p>TableGen offers diverse ways to assign a value to a field. Some ways take precedence over others, and since they are all used extensively in the LLVM code base, it is important you know about them.</p><p>You already know two of them:</p><ul><li><p>Assignment via multi-class/class arguments</p></li><li><p>Plain assignment (e.g., with a literal/argument)</p></li></ul><p>There is a third way that you need to know; it uses the <code>let</code> keyword with or without the <code>in</code> keyword.</p><p>The <code>let</code> keyword allows to override the values of a list of fields for the specified context. Without the <code>in</code> keyword, the context is the current record or class.</p><p>When used in a record or class, the syntax of a <code>let</code> statement is as follows:</p><pre><code>let fieldName1 = newVal1[, fieldName2 = newVal2]*;</code></pre><p>When using a <code>let</code> keyword in a record or class, all <code>fieldNameX</code> instances must have been previously defined. The <code>let</code> keyword assigns <code>newValX</code> to <code>fieldNameX</code>.</p><p>Now, if you want to define a context for the effect of <code>let</code>, you use the following construct:</p><pre><code>let FieldName1 = NewVal1[, FieldName2 = NewVal2]* in</code></pre><p>Compared to the version without the <code>in</code> keyword, field names will be resolved in the context of the statement right after <code>in</code>. This means that <code>fieldNameX</code> can reference field names that are yet to be defined. Additionally, the statement right after the <code>in</code> keyword can be arbitrarily large by encompassing it with <code>{</code> and <code>}</code> characters.</p><p>This means you can override the values of many fields for many records at once.</p><p>Finally, let&#8217;s define the order of evaluation of the ways of assigning a value. Since the <code>let</code> keyword is used for overriding, it is evaluated after the regular field assignments (i.e., any non-<code>let</code> assignments are resolved first). Now, if there are several <code>let</code>-statements that affect the same field in the same context, the last one, in a file order from top to bottom, wins.</p><p>To make this clearer, consider the following example:</p><pre><code>class MyClass&lt;string _alias=""&gt; {
  string alias = _alias;
}
let alias = "let from out" in
def A: MyClass&lt;&gt; {}
def B: MyClass&lt;&gt; {
  let alias = "let from body";
}
def C: MyClass&lt;"from arg"&gt;;
let alias = "alias from bigger scope" in {
let alias = "let from out" in
def D: MyClass&lt;"from arg"&gt; {
  let alias = "let from body";
}
def E: MyClass&lt;"will be overridden"&gt;;
} // end "alias from bigger scope"</code></pre><p>In this example, you see the different assignment mechanisms in action on the field named alias:</p><ul><li><p>For record <code>A</code>, <code>alias</code> is overridden with a <code>let</code>-statement right after the definition of the record. <code>alias</code> will have the <code>"let from out"</code> value.</p></li><li><p>For record <code>B</code>, <code>alias</code> is overridden within the body of the record. It will have the <code>"let from body"</code> value.</p></li><li><p>For record <code>C</code>, we use a regular assignment (<code>alias = _alias</code> from within <code>MyClass</code>) after passing the value we want, <code>"from arg"</code>, as a class argument.</p></li><li><p>For record <code>D</code>, focus on the <code>let</code> statements, since they override everything else. Then, find the last one &#8211; <code>alias</code> will have the <code>"let from body"</code> value.</p></li><li><p>Record <code>E</code> looks innocent at first, but note that we are still within the context of the <code>let</code> statement that started slightly before record <code>D</code>. As such, the value of <code>alias</code> will have the value of this <code>let</code> statement (i.e., <code>"alias from bigger scope"</code>).</p></li></ul><p>This concludes the basic skills you need to navigate the <code>.td</code> files in the LLVM code base. You will see that TableGen offers some other constructs that you should be already familiar with, such as comments (<code>//</code> and <code>/*</code> <code>*/</code>) and includes (<code>include "path"</code>), but you should already be familiar with these concepts.</p><blockquote><p>Note</p><p>In TableGen, there is no concept of header files. As such, you will not find any guard construct (the <code>ifndef</code>/<code>define</code> construct in the C-world), and includes flow transitively in the included files. This means two things. First, if you include a file twice, even transitively (e.g., if <code>A</code> includes <code>B</code> and <code>C</code>, and <code>B</code> includes <code>D</code>, and <code>C</code> also includes <code>D</code>, it implies that <code>A</code> includes <code>D</code> twice (once through <code>B</code> and once through <code>C</code>)), you will end up with a lot of errors around redefined records/classes. If that happens, double-check your includes in the global context. Second, it is okay to assume that a bigger context includes what you need in your <code>.td</code> file and materialize the dependencies accordingly (e.g., for the previous example, you would have <code>A</code> includes <code>B</code>, <code>C</code> and <code>D</code>, <code>B</code> includes nothing, and <code>C</code> includes nothing; <code>D</code> needs to be included before <code>B</code> and <code>C</code> in <code>A</code>).</p></blockquote><p>In this section, you learned how to read, write, and understand TableGen files. You got accustomed to the not-so-intuitive <code>multiclass</code> and <code>defm</code> constructs, and you saw how to leverage <code>let</code> statements to modify your records after the fact.</p><p>The next section will prime you on the usage of TableGen in the LLVM infrastructure by presenting a TableGen backend used in LLVM.</p><div><hr></div><h1>Discovering a TableGen backend</h1><p>Although we could have fun creating records all day long, there is not much point to it if we don&#8217;t do anything with them. Therefore, in this section, you will discover one of the TableGen backends used to build LLVM &#8211; in other words, how records that you create are consumed and used in LLVM.</p><p>While the content of this section remains high-level, we believe understanding the purpose of the records that you will build makes you more effective at it. First, it will help you build a mental model of the inner workings of TableGen, and second, it will give you confidence in approaching new TableGen backends.</p><p>Let&#8217;s start this section with some information that applies to all the TableGen backends that target LLVM.</p><h2>General information on TableGen backends for LLVM</h2><p>If you are building several projects within the LLVM umbrella, you will see that different projects use different TableGen drivers. For instance, while LLVM proper has <code>llvm-tblgen</code>, Clang has <code>clang-tblgen</code>, and MLIR has <code>mlir-tblgen</code>.</p><p>The main difference between these drivers is the TableGen backends they offer. Otherwise, the frontend is the same for all of them, meaning that what you learned on the TableGen syntax applies to all of them as well.</p><p>In this book, you will interact solely with the <code>llvm-tblgen</code> tool (i.e., the driver of the LLVM project).</p><p>Using this tool, you can see all the available backends by running the following on the command line:</p><pre><code>$ llvm-tblgen --help</code></pre><p>This will print a list of all the command-line options supported by the tool and the <code>--gen-xxx</code> options that, by convention, represent the available backends.</p><p>From that point, you can start playing with the different backends.</p><p>Now, more realistically, you do not want to start playing with a specific backend from scratch. Indeed, you need to provide certain records for the backends to do what they are meant to do. (You will learn what these records are in the relevant upcoming chapters.) Moreover, these records rely on classes that are provided by the core LLVM infrastructure, and you do not want to rediscover where these live and so on. Put differently, you do not want to rediscover the build dependencies manually. Instead, you want to leverage the build system to provide you with the right command for a specific backend.</p><p>If you are dealing with an issue with TableGen, chances are your build system will fail while building the related <code>.inc</code> file, so you will get this command line easily. If not, you can rebuild this <code>.inc</code> file with the right build target, as explained in the <em>A Crash course on Ninja</em> section in .</p><p>For instance, here is the command line reported by the build system for the <code>lib/Target/AArch64/AArch64GenInstrInfo.inc</code> target (i.e., the <code>instr-info</code> backend when run for the AArch64 backend):</p><pre><code>$ llvm-tblgen -gen-instr-info -I ${LLVM_SRC}/llvm/lib/Target/AArch64 -I${LLVM_BUILD}/include -I ${LLVM_SRC}/llvm/include -I ${LLVM_SRC}/llvm/lib/Target ${LLVM_SRC}/llvm/lib/Target/AArch64/AArch64.td --write-if-changed -o lib/Target/AArch64/AArch64GenInstrInfo.inc -d lib/Target/AArch64/AArch64GenInstrInfo.inc.d</code></pre><p>Irrespective of how you get your <code>llvm-tblgen</code> command line, the point is that this is your starting point for further exploration or debugging.</p><p>For existing LLVM backends, you can further check how a <code>.inc</code> file fits in the overall picture by looking at how it&#8217;s used.</p><p>For instance, the following command line, run at the root of <code>${LLVM_SRC}</code>, gives all the files that directly use <code>AArch64GenInstrInfo.inc</code>:</p><pre><code>$ git grep -l 'AArch64GenInstrInfo\.inc"' -- llvm</code></pre><p>Now, you know how to discover what backends exist and how to find the proper command-line invocation for them.</p><p>The next section takes you a little bit deeper into the world of a TableGen backend.</p><h2>Discovering a TableGen backend</h2><p>Sadly, most TableGen backends are under-documented. The good news is that all of them are used by at least one open source target. Using this open source target, you can see what the related <code>.inc</code> file looks like, and you can forge a mental model of how the related <code>.td</code> files connect to this output.</p><p>This section presents what one TableGen backend generates. This information will help you build some reflexes of how to approach TableGen backends that you have never worked with. By seeing how a backend works, you will learn what to look for when approaching a new backend.</p><p>We focused on one of the backends you will use later in this book, but we did not document all the ones that you will use. However, you can use the same approach to reverse-engineer how all the TableGen backends work.</p><p>Let&#8217;s now focus on the TableGen backend used to generate the information for the <em>intrinsics</em>.</p><h3>The implementation of intrinsics</h3><p>At the LLVM <strong>intermediate representation</strong> (<strong>IR</strong>) level, <strong>intrinsics</strong> are special functions known by the compiler. They usually map to specific target instructions. For instance, the LLVM function named <code>llvm.aarch64.ldxr</code> is an intrinsic at the LLVM IR level that maps to the <code>LDXRB</code> instruction of the AArch64 backend.</p><p>The TableGen backend that we cover here generates the LLVM IR boilerplate to use this intrinsic. The mapping to the final instruction is outside of the scope of this backend. You can invoke this backend through the <code>llvm-tblgen</code> executable by using the <code>-gen-intrinsic-impl</code> option.</p><p>You can look at the generated boilerplate by opening <code>${LLVM_BUILD}/include/llvm/IR/IntrinsicImpl.inc</code>.</p><p>Let&#8217;s look at the content of this file.</p><h3>The content of a generated file</h3><p>Looking into <code>IntrinsicImpl.inc</code>, the interesting bits are how the generated code is structured. The file is virtually split into several sections. Each section is guarded by a macro, <code>#ifdef GET_[LLVM_]INTRINSIC_XXX</code>, where <code>XXX</code> is, in this case, one of <code>IITINFO</code>, <code>TARGET_DATA</code>, <code>NAME_TABLE</code>, and so on. Each section has its own usage ,as described in <em>Table 6.1</em>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GTQV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F183246fd-2267-4d6d-b04f-008a5968b525_1341x611.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GTQV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F183246fd-2267-4d6d-b04f-008a5968b525_1341x611.png 424w, https://substackcdn.com/image/fetch/$s_!GTQV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F183246fd-2267-4d6d-b04f-008a5968b525_1341x611.png 848w, https://substackcdn.com/image/fetch/$s_!GTQV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F183246fd-2267-4d6d-b04f-008a5968b525_1341x611.png 1272w, https://substackcdn.com/image/fetch/$s_!GTQV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F183246fd-2267-4d6d-b04f-008a5968b525_1341x611.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GTQV!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F183246fd-2267-4d6d-b04f-008a5968b525_1341x611.png" width="1200" height="546.7561521252796" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/183246fd-2267-4d6d-b04f-008a5968b525_1341x611.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:611,&quot;width&quot;:1341,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:116325,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/169732298?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F183246fd-2267-4d6d-b04f-008a5968b525_1341x611.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GTQV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F183246fd-2267-4d6d-b04f-008a5968b525_1341x611.png 424w, https://substackcdn.com/image/fetch/$s_!GTQV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F183246fd-2267-4d6d-b04f-008a5968b525_1341x611.png 848w, https://substackcdn.com/image/fetch/$s_!GTQV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F183246fd-2267-4d6d-b04f-008a5968b525_1341x611.png 1272w, https://substackcdn.com/image/fetch/$s_!GTQV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F183246fd-2267-4d6d-b04f-008a5968b525_1341x611.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Table 6.1: Macros that guard the generated output of the IntrinsicImpl TableGen backend</em></p><p>If you look at the other TableGen backends used by LLVM, you will see the same structure &#8211; virtual sections enabled by different macros and included in various parts of the LLVM infrastructure.</p><p>For this TableGen backend, you can find these usages by looking at how <code>IntrinsicImpl.inc</code> is included throughout the LLVM code base.</p><p>Let&#8217;s now look at what drives this backend.</p><h3>The source of a TableGen backend</h3><p>The main source file used by the <code>IntrinsicImpl</code> TableGen backend in the LLVM build system is <code>${LLVM_SRC}/llvm/include/llvm/IR/Intrinsics.td</code>.</p><p>This file contains the classes and records that are used to define all the target-specific intrinsics.</p><p>The thing that may be difficult to untangle with TableGen is that the backend draws some information from specific classes and/or records. In other words, records can be more than just an instantiation of a class.</p><p>For instance, consider the following snippet from <code>Intrinsics.td</code>:</p><pre><code>def IntrArgMemOnly : IntrinsicProperty;
def IntrInaccessibleMemOnly : IntrinsicProperty;</code></pre><p>These two records have the same type (<code>IntrinsicProperty</code>) and carry the same field values. Therefore, you might think that they are interchangeable, but this is not true!</p><p>The name of certain records carries special significance for the related backend.</p><p>In this case, the <code>IntrinsicEmitter</code> class (located in <code>llvm/utils/TableGen</code>), which implements this TableGen backend, uses these records to classify the memory effects of the intrinsics and generates different C++ code based on that.</p><p>For instance, you can see from the following snippet from <code>IntrinsicsAArch64.td</code> how <code>IntrArgMemOnly</code> can be used to define a particular intrinsic:</p><pre><code>def int_aarch64_settag : DefaultAttrsIntrinsic&lt;[], [llvm_ptr_ty, llvm_i64_ty], [IntrWriteMem, IntrArgMemOnly, NoCapture&lt;ArgIndex&lt;0&gt;&gt;, WriteOnly&lt;ArgIndex&lt;0&gt;&gt;]&gt;;</code></pre><p>The takeaway is that by only looking at a <code>.td</code> file, it is impossible to determine what are considered built-in constructs for a specific backend and what are just the uses of these constructs!</p><p>To remediate that problem, the authoring of TableGen files in LLVM is structured such that important records and classes are in one main include file (here <code>Intrinsics.td</code>) and the instantiation of these concepts is in target-specific files (here <code>IntrinsicsXXX.td</code> where <code>XXX</code> is the name of the related backend.) What this means is the classes and records that you may find in <code>IntrinsicsXXX.td</code> are specific to these files and are not load-bearing for the TableGen backend itself. In other words, you can interpret them as regular records and classes (i.e., a way to share information between records). The records and classes defined in the generic files usually hold a lot of comments that will help you instantiate your own records.</p><p>What you saw in this section applies to all TableGen backends in LLVM. For instance, if you look at <code>${LLVM_BUILD}/lib/Target/AArch64/AArch64GenGlobalISel.inc</code>, which is the output of the <code>GlobalISel</code> selector (<code>-gen-global-isel</code>) TableGen backend, you will find the guarded sections (e.g., <code>GET_GLOBALISEL_PREDICATE_BITSET</code> and <code>GET_GLOBALISEL_TEMPORARIES_DECL</code>), the load-bearing records and classes (in <code>${LLVM_SRC}/llvm/include/llvm/Target/Target.td</code>), and the LLVM-backend-specific implementation (in <code>${LLVM_SRC}/llvm/lib/Target/AArch64/AArch64.td</code>).</p><p>In this section, you learned that, sadly, the best way to approach a TableGen backend is to look at what was implemented in another LLVM backend. However, by discovering the general principles behind each TableGen backend used in LLVM, you learned how to follow basic threads to forge an understanding of the main classes and records leveraged by the related backends.</p><p>The next section covers tips and tricks to help you troubleshoot issues with a TableGen backend.</p><div><hr></div><h1>Debugging the TableGen framework</h1><p>When you interact with TableGen, it may not be immediately obvious what part of TableGen fails you, or how you misused it!</p><p>In this section, you will learn ways to determine what goes wrong. The way to fix the problem may, however, be backend-specific, and given the sheer volume of TableGen backends, we will not cover this in this book. However, we give you general guidance on how to approach them.</p><p>Let&#8217;s start with the basics!</p><h2>Identifying the failing component</h2><p>As you learned in <em>Getting started with TableGen</em>, a TableGen workflow implies several components &#8211; the frontend, the backend, and the use of the generated output in LLVM.</p><p>To identify which component is at fault, you need to identify when the failure occurs:</p><ul><li><p>If the failure occurs while producing the <code>.inc</code> file, the problem is either with the TableGen frontend or backend, the syntax you used, or how you used the specific constructs (class or record) of this backend.</p></li><li><p>If the failure occurs while building a part of LLVM that includes the <code>.inc</code> file, the problem is with the TableGen backend, how you used the specific constructs of this backend, or the code that uses that generated file.</p></li><li><p>If the failure occurs while running the compiler, the problem is with the TableGen backend, the code that uses it, or how you used the specific constructs of this backend.</p></li></ul><p>Most of the TableGen backends have been around for a while and used quite a lot. Therefore, if something breaks at build time but after the <code>.inc</code> file has been generated (case 2) or at runtime (case 3), chances are the issue is in how you described your records. If you cannot find the issue yourself or believe there is a bug in the related TableGen backend, engage with the LLVM community. You may, for instance, help future developers by making the related TableGen backend produce a warning for the problematic case, instead of generating something dubious!</p><p>This leaves us with case 1 &#8211; TableGen failing to generate the <code>.inc</code> file.</p><p>At this point, TableGen should report an error. Your job is to find out whether this error comes from the TableGen frontend or backend.</p><p>To easily identify whether you are dealing with a frontend or a backend error, do the following:</p><ol><li><p>Find the command line that generates the <code>.inc</code> file (refer back to the <em>General information on TableGen backends for LLVM</em> section).</p></li><li><p>Remove the <code>-gen-xxx</code> options.</p></li><li><p>Add the <code>-print-records</code> option.</p></li><li><p>Run the resulting command.</p></li></ol><p>If the error remains, you are dealing with a TableGen frontend error. In other words, your syntax is incorrect. Usually, the frontend errors come with a line and column number referencing the input file.</p><p>If the error disappears, you are dealing with a backend-specific error, which we will discuss in the next section.</p><h2>Cracking open a TableGen backend</h2><p>Most of the time, the error reported by a TableGen backend gives you enough information to infer what you need to do to solve it.</p><p>For instance, imagine you want to use the GlobalISel selector backend and you get the following error:</p><pre><code>error: The class 'HwMode' is not defined</code></pre><p>This is straightforward &#8211; you miss a record of the <code>HwMode</code> class in your <code>.td</code> file for the backend to be able to operate.</p><p>To find this class, you can leverage <code>git grep</code> (see the following code snippet), or you can follow the include directory (<code>-I</code> option) to find the <code>.td</code> file where the important classes and records are defined (in this case, <code>${LLVM_SRC}/llvm/include/llvm/Target/Target.td</code>):</p><pre><code>$ cd ${LLVM_SRC}; git grep 'class HwMode' -- llvm/include/llvm | grep '\.td'</code></pre><p>The previous command goes into the <code>${LLVM_SRC}</code> directory, looks for the definition of the <code>HwMode</code> class, and filters the result such that only <code>.td</code> files are printed.</p><p>Then, later, let&#8217;s say the next error is as follows:</p><pre><code>input.td:13:1: error: Record `anonymous_7223', field `InstructionSet' does not have a def initializer!</code></pre><p>This time, this is a frontend error that tells us that the <code>InstructionSet</code> field is not initialized. In other words, this means we did not set a value for that field but we should have. (There is no default initializer in this case.) We just need to check what the type of <code>InstructionSet</code> is to see how to instantiate a record appropriately and move on.</p><p>After all that, if you still encounter errors that may not have an obvious fix and the community cannot help you, this is where the fun starts!</p><p>If you are lucky, the backend features a rich enough debug log for you to understand what is going on. To enable it, add <code>-debug</code> to the <code>llvm-tblgen</code> command line.</p><p>If this is not enough, you must dig into the C++ code of the related backend. All the backends live under <code>${LLVM_SRC}/llvm/utils/TableGen</code>.</p><p>When looking at the code of a TableGen backend, here are a few things you should know:</p><ul><li><p>The entry point is usually a <code>run</code> method on the related <code>XXXEmitter</code> class.</p></li><li><p>The load-bearing records and classes are usually collected through calls to <code>RecordKeeper::getAllDerivedDefinitions</code>; looking at the arguments of this function gives you the names of these records and classes.</p></li><li><p>The helper structures used throughout the backends live in private headers under <code>${LLVM_SRC}/llvm/utils/TableGen/Common</code>.</p></li></ul><p>Overall, although what is generated by the TableGen backends may be complicated, following how they do the generation is relatively simple. First, they traverse some records stored in the main <code>RecordKeeper</code> object provided to them. Second, they generated the final output based on the values of the fields of these records.</p><p>In this section, you learned the basic skills required to approach an unknown TableGen backend. You saw that, if possible, you should use an existing user (i.e., an open source LLVM backend) of this backend as a template, and if this is not possible, you saw how to break down the problem into stages to isolate where the problem comes from. Finally, you got a few tips on how to approach the C++ code of a TableGen backend.</p><div><hr></div><h1>Summary</h1><p>While we could spend a lot more time describing TableGen and its backends, we believe this chapter gave you enough material for you to get started in this space. Also, while understanding how a TableGen backend works can be satisfying, it is not required to write an LLVM backend. Hence, you just learned the basic skills that are necessary to write the inputs of these backends.</p><p>At this point, you may not feel comfortable writing records for a specific backend or know how the TableGen output of a backend fits into the LLVM infrastructure, and this is expected. You will grow more confident and accumulate this knowledge in the respective chapters when targeting these backends. However, now, you should feel confident looking at TableGen files (<code>.td</code>), and although you may not understand what the records are meant for, you should be able to predict what their content is.</p><p>In conclusion, you learned the following in this chapter:</p><ul><li><p>What TableGen is and the general principle of how it is used in LLVM</p></li><li><p>How to read and write your first TableGen inputs</p></li><li><p>How the TableGen backends work and the basic structure of</p><ul><li><p>their inputs, including where to find the load-bearing records and classes</p></li><li><p>their outputs, with their different sections guarded by macros</p></li></ul></li><li><p>How to deal with errors with TableGen</p></li></ul><p>This chapter concludes the basic knowledge you need to efficiently develop a backend with the LLVM infrastructure. In the next chapter, we will start to go deeper into the world of compilers by focusing our attention on understanding the LLVM IR.</p><div><hr></div><h1>Further reading</h1><p>This chapter gave you a primer on TableGen. We did not cover things that you will probably never do, such as developing your own TableGen backend. If you want to explore TableGen-related topics in more detail, the LLVM&#8217;s documentation, while not perfect, covers some of these.</p><p>You can refer to the following:</p><ul><li><p>An overview of TableGen at <a href="https://llvm.org/docs/TableGen/">https://llvm.org/docs/TableGen/</a>.</p></li><li><p>The full specification of the TableGen language at <a href="https://llvm.org/docs/TableGen/ProgRef.html.">https://llvm.org/docs/TableGen/ProgRef.html.</a></p></li><li><p>The (succinct) documentation of the TableGen backends at <a href="https://llvm.org/docs/TableGen/BackEnds.html">https://llvm.org/docs/TableGen/BackEnds.html</a>.</p></li><li><p>How to develop a TableGen backend at <a href="https://llvm.org/docs/TableGen/BackGuide.html">https://llvm.org/docs/TableGen/BackGuide.html</a>. This one is also interesting if you want to debug a TableGen backend, since it presents the main classes available to handle records.</p></li><li><p>The command-line guide for the <code>llvm-tblgen</code> tool at <a href="https://llvm.org/docs/CommandGuide/tblgen.html">https://llvm.org/docs/CommandGuide/tblgen.html</a>.</p></li><li><p>Finally, the compiler explorer website (https://godbolt.org/) features a TableGen mode for you to play with the TableGen syntax.</p></li></ul><p>Before you finish this chapter, check out the quiz to test what you remember!</p><div><hr></div><h1>Quiz time</h1><p>Now that you have completed reading this chapter, try answering the following questions to test your knowledge:</p><ol><li><p>What does the following snippet do?</p></li></ol><pre><code><code>def;</code></code></pre><p>This creates an empty record, and TableGen automatically assigns it a unique name.</p><p>See the <em>The TableGen programming language</em> and <em>Defining multiple records at once </em>sections.</p><ol start="2"><li><p>What would happen if we took the snippet featured in the <em>Defining multiple records at once</em> section and replaced the definition of the <code>ShippingPrice</code> class with the following snippet?</p></li></ol><pre><code><code>class ShippingPrice&lt;int arg&gt; {
  int price = arg;
}</code></code></pre><p>We replaced the field named <code>shippingPrice</code> with a field named <code>price</code>. The new name now collides with the <code>price</code> field of record <code>A</code>. As a result, the value of record <code>A</code>'s <code>price</code> will be overridden by the value of the <code>price</code> field of the <code>ShippingPrice</code> class.</p><p>See the <em>The TableGen programming language</em> section for more information.</p><ol start="3"><li><p>How can you check whether a TableGen error comes from the frontend or the backend?</p></li></ol><p>First, frontend errors usually report the filename, the line, and column numbers. Second, the frontend errors are not affected by the presence of/lack of <code>-gen-xxx</code> options.</p><p>See the <em>Identifying the failing component</em> section.</p><ol start="4"><li><p>How would you approach working with an existing TableGen backend for the first time?</p></li></ol><p>In a nutshell, you would look for another LLVM backend where this TableGen backend is used, read the comment in the main <code>.td</code> file that supports this TableGen backend, ask the community, and if necessary, look at the code of the TableGen backend.</p><p>See the <em>Discovering a TableGen backend</em> section.</p><ol start="5"><li><p>How is the output of a TableGen backend typically structured?</p></li></ol><p>The output of a TableGen backend is usually saved in a <code>.inc</code> file that is later included in various LLVM C++ files. The content of the file features several sections, each guarded by an <code>ifdef</code> macro.</p><p>See the <em>The content of a generated file</em> section.</p><div><hr></div><p>To learn how to design instruction selectors, build legalizer stages, and debug backend passes with LLVM&#8217;s own reduction tools&#8212;check out <em><strong><a href="https://www.packtpub.com/en-us/product/llvm-code-generation-9781835462577">LLVM Code Generation</a></strong></em> by Quentin Colombet, available from Packt. This 620-page comprehensive guide walks readers through the internals of LLVM&#8217;s backend infrastructure, from transforming IR to generating optimized machine code. With step-by-step examples, targeted exercises, and hands-on walkthroughs using TableGen, Machine IR, and GlobalISel, it&#8217;s both a reference and a roadmap for backend developers working on real-world architectures. Whether you&#8217;re building a custom target, contributing to LLVM itself, or deepening your compiler expertise, this book provides a practical foundation for mastering the backend.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.packtpub.com/en-us/product/llvm-code-generation-9781835462577" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VMSy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba154a1-1adc-42aa-9613-d830b86b54fc_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!VMSy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba154a1-1adc-42aa-9613-d830b86b54fc_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!VMSy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba154a1-1adc-42aa-9613-d830b86b54fc_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!VMSy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba154a1-1adc-42aa-9613-d830b86b54fc_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VMSy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba154a1-1adc-42aa-9613-d830b86b54fc_2250x2775" width="288" height="355.25274725274727" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dba154a1-1adc-42aa-9613-d830b86b54fc_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:288,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;LLVM Code Generation&quot;,&quot;title&quot;:&quot;LLVM Code Generation&quot;,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.packtpub.com/en-us/product/llvm-code-generation-9781835462577&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="LLVM Code Generation" title="LLVM Code Generation" srcset="https://substackcdn.com/image/fetch/$s_!VMSy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba154a1-1adc-42aa-9613-d830b86b54fc_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!VMSy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba154a1-1adc-42aa-9613-d830b86b54fc_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!VMSy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba154a1-1adc-42aa-9613-d830b86b54fc_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!VMSy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba154a1-1adc-42aa-9613-d830b86b54fc_2250x2775 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here is what some readers have said:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bkDn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0abfc8de-2e45-4f27-b10e-eb3804ff3c20_873x538.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bkDn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0abfc8de-2e45-4f27-b10e-eb3804ff3c20_873x538.png 424w, https://substackcdn.com/image/fetch/$s_!bkDn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0abfc8de-2e45-4f27-b10e-eb3804ff3c20_873x538.png 848w, https://substackcdn.com/image/fetch/$s_!bkDn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0abfc8de-2e45-4f27-b10e-eb3804ff3c20_873x538.png 1272w, https://substackcdn.com/image/fetch/$s_!bkDn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0abfc8de-2e45-4f27-b10e-eb3804ff3c20_873x538.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bkDn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0abfc8de-2e45-4f27-b10e-eb3804ff3c20_873x538.png" width="873" height="538" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0abfc8de-2e45-4f27-b10e-eb3804ff3c20_873x538.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:538,&quot;width&quot;:873,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:103726,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/169655369?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0abfc8de-2e45-4f27-b10e-eb3804ff3c20_873x538.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!bkDn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0abfc8de-2e45-4f27-b10e-eb3804ff3c20_873x538.png 424w, https://substackcdn.com/image/fetch/$s_!bkDn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0abfc8de-2e45-4f27-b10e-eb3804ff3c20_873x538.png 848w, https://substackcdn.com/image/fetch/$s_!bkDn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0abfc8de-2e45-4f27-b10e-eb3804ff3c20_873x538.png 1272w, https://substackcdn.com/image/fetch/$s_!bkDn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0abfc8de-2e45-4f27-b10e-eb3804ff3c20_873x538.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!krGS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f9b39a-683f-417d-a478-ff299ffe91a6_887x372.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!krGS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f9b39a-683f-417d-a478-ff299ffe91a6_887x372.png 424w, https://substackcdn.com/image/fetch/$s_!krGS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f9b39a-683f-417d-a478-ff299ffe91a6_887x372.png 848w, https://substackcdn.com/image/fetch/$s_!krGS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f9b39a-683f-417d-a478-ff299ffe91a6_887x372.png 1272w, https://substackcdn.com/image/fetch/$s_!krGS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f9b39a-683f-417d-a478-ff299ffe91a6_887x372.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!krGS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f9b39a-683f-417d-a478-ff299ffe91a6_887x372.png" width="887" height="372" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/21f9b39a-683f-417d-a478-ff299ffe91a6_887x372.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:372,&quot;width&quot;:887,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:74521,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/169655369?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f9b39a-683f-417d-a478-ff299ffe91a6_887x372.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!krGS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f9b39a-683f-417d-a478-ff299ffe91a6_887x372.png 424w, https://substackcdn.com/image/fetch/$s_!krGS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f9b39a-683f-417d-a478-ff299ffe91a6_887x372.png 848w, https://substackcdn.com/image/fetch/$s_!krGS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f9b39a-683f-417d-a478-ff299ffe91a6_887x372.png 1272w, https://substackcdn.com/image/fetch/$s_!krGS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f9b39a-683f-417d-a478-ff299ffe91a6_887x372.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GW-A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8616fcc9-8ca3-4932-af60-2c746c1f49e5_863x207.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GW-A!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8616fcc9-8ca3-4932-af60-2c746c1f49e5_863x207.png 424w, https://substackcdn.com/image/fetch/$s_!GW-A!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8616fcc9-8ca3-4932-af60-2c746c1f49e5_863x207.png 848w, https://substackcdn.com/image/fetch/$s_!GW-A!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8616fcc9-8ca3-4932-af60-2c746c1f49e5_863x207.png 1272w, https://substackcdn.com/image/fetch/$s_!GW-A!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8616fcc9-8ca3-4932-af60-2c746c1f49e5_863x207.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GW-A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8616fcc9-8ca3-4932-af60-2c746c1f49e5_863x207.png" width="863" height="207" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8616fcc9-8ca3-4932-af60-2c746c1f49e5_863x207.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:207,&quot;width&quot;:863,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:43173,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/169655369?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8616fcc9-8ca3-4932-af60-2c746c1f49e5_863x207.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!GW-A!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8616fcc9-8ca3-4932-af60-2c746c1f49e5_863x207.png 424w, https://substackcdn.com/image/fetch/$s_!GW-A!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8616fcc9-8ca3-4932-af60-2c746c1f49e5_863x207.png 848w, https://substackcdn.com/image/fetch/$s_!GW-A!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8616fcc9-8ca3-4932-af60-2c746c1f49e5_863x207.png 1272w, https://substackcdn.com/image/fetch/$s_!GW-A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8616fcc9-8ca3-4932-af60-2c746c1f49e5_863x207.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Go 1.25 JSON v2: Long-Awaited JSON Package Revamp]]></title><description><![CDATA[A closer look at the performance, error handling, and streaming improvements.]]></description><link>https://deepengineering.substack.com/p/go-125-json-v2-long-awaited-json</link><guid isPermaLink="false">https://deepengineering.substack.com/p/go-125-json-v2-long-awaited-json</guid><dc:creator><![CDATA[Alexander Shuiskov]]></dc:creator><pubDate>Wed, 30 Jul 2025 13:31:18 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/434462b2-a024-492c-be84-a6445d53c1e9_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Nearly every Go developer has worked with the built-in <code>encoding/json</code> package that has been in the standard library since its initial release. Over the years, the package has been widely criticized for performance bottlenecks, limited flexibility, and poor diagnostics. As a response, the Go team recently <a href="https://tip.golang.org/doc/go1.25#json_v2">released</a> a major update, <code>encoding/json/v2</code>, the updated experimental v2 version of the package. </p><p>In this article, we will discuss the main motivation for a revamp of the built-in JSON package and see how you can leverage the new features of the updated v2 package.</p><h2><strong>The JSON v1 Pain: Why a Rewrite Was Needed</strong></h2><p>Here are some of the most common frustrations with the original <code>encoding/json</code> package:</p><ul><li><p><strong>Performance</strong>: Go built-in JSON library has been 2-5x slower in encoding and decoding JSON structures than many third-party libraries.</p></li><li><p><strong>Non-descriptive error messages</strong>: In cases of complex or nested structures, error messages don&#8217;t provide enough context, requiring to validate entire input manually.</p></li><li><p><strong>Limited control</strong>: Handling different edge cases such as rejecting unknown fields in JSON structures has always been a challenge.</p></li><li><p><strong>No streaming for standard types</strong>: You couldn&#8217;t decode large arrays from a stream without fully loading them in memory first.</p></li></ul><p>These issues have been known for years and heavily discussed in the Go community, including<a href="https://github.com/golang/go/discussions/63397"> this popular thread</a>.</p><h2><strong>JSON v2: A Clean(er) Slate</strong></h2><p>Go 1.25 introduced <code>encoding/json/v2</code> as an <strong>experimental</strong> package with the following goals:</p><ul><li><p>More descriptive error messages</p></li><li><p>Streaming support</p></li><li><p>Safer and more predictable encoding/decoding</p></li><li><p>Extensible hooks for advanced use cases</p></li></ul><p>To use the updated version you must: </p><ol><li><p>Explicitly import <code>encoding/json/v2</code>. </p></li><li><p>Set the <code>GOEXPERIMENT=jsonv2</code> environment variable</p></li></ol><p>The API might change in the following releases. Yet, even at this early stage, it offers significant improvements.</p><h2><strong>Practical Examples: JSON v1 vs JSON v2</strong></h2><p>Let&#8217;s walk through some common use cases and see how JSON v2 makes things better.</p><h3><strong>Better Errors with Field Context</strong></h3><p>First, let&#8217;s run this example using the original version of <code>encoding/json</code> package:</p><pre><code>package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Age int `json:"age"`
}

type Data struct {
    Users []User `json:"users"`
}

func main() {
    data := []byte(`{"users": [{"age": 30}, {"age": "twenty"}]}`)

    var users Data

    err := json.Unmarshal(data, &amp;users)
    fmt.Println(err)
}</code></pre><p>The output will be:</p><pre><code><code>json: cannot unmarshal string into Go struct field User.users.age of type int</code></code></pre><p>From the output, we aren&#8217;t able to tell what element of the input array caused the error. If the input array contains hundreds of elements, finding an error case would take time.</p><p>Let&#8217;s switch to v2 version of the JSON package by importing (note that you also need to set <code>GOEXPERIMENT=jsonv2</code> environment variable to enable JSON v2):</p><pre><code><code>import "encoding/json/v2"</code></code></pre><p>After running the same code, we will now see</p><pre><code><code>json: cannot decode string into int at "users[1].age"</code></code></pre><p>The output includes the index of an incorrect element in the array &#8212; a huge win for debugging.</p><h3><strong>Streaming Large Arrays</strong></h3><p>With <code>json/v2</code>, you can stream JSON arrays from disk or network:</p><pre><code>f, _ := os.Open("large.json")
defer f.Close()

dec := json.NewDecoder(f)

tok, _ := dec.ReadToken() // Read start of array token (e.g., json.StartArray)

for dec.PeekKind() != json.EndArray {
    var entry MyStruct

    if err := dec.Decode(&amp;entry); err != nil {
        log.Fatal(err)
    }

    process(entry)
}

dec.ReadToken() // Read json.EndArray token</code></pre><p>This avoids loading the entire JSON array into memory&#8212;ideal for large datasets.</p><h3><strong>Struct Tag Extensions</strong></h3><p><code>json/v2</code> adds several helpful options to reduce boilerplate:</p><pre><code>type Payment struct {
    Amount   int    `json:",intstring"`  // Parses "1000" as int 1000
    Currency string `json:",nullable"`   // Accepts null or string
    Optional string `json:",omitempty"`  // Omits the field if empty
}</code></pre><p>Using these options helps to avoid extra steps when decoding structures, such as creating intermediate data structures and converting types (for example, strings and ints).</p><h3><strong>Performance</strong></h3><p>The <code>json/v2</code> package brings major performance improvements, offering <strong>zero-heap decoding</strong> for many struct types. This means it can populate Go values directly on the stack, avoiding memory allocations. This significantly boosts throughput and reduces GC pressure in performance-critical code.</p><p>In decoding benchmarks, <code>json/v2</code> is <a href="https://github.com/golang/go/issues/71497">reported</a> to be <strong>2-10x faster</strong> than the original <code>encoding/json</code>, primarily due to a more efficient parser.</p><p>If you need even more performance, you might consider streaming JSON decoder that offered up to <strong>40x speedup</strong> for certain large struct types. Though not fully reflected in standard benchmarks, these gains become apparent in deeply nested or recursive use cases.</p><p>Compared to popular high-performance third-party libraries like <code>jsoniter</code>, <code>go-json</code>, or <code>segmentio/json</code>, the new standard library is now in the same performance class &#8212; a significant milestone for production-grade efficiency without external dependencies.</p><h2><strong>Conclusion: Should You Use JSON v2?</strong></h2><p>Keep in mind that <code>encoding/json/v2</code> package is still experimental and might change in the upcoming releases. If you decide to use it, keep its scope limited to specific scenarios (for example, high-performance or large-volume JSON processing and any advance use cases requiring customization) and track the following changes in the package API.</p><p>You may find more useful details on the v2 package in the following links:</p><ul><li><p>JSON v2 proposal issue: <a href="https://github.com/golang/go/issues/71497">https://github.com/golang/go/issues/71497</a></p></li><li><p>Package documentation: <a href="https://pkg.go.dev/encoding/json/v2">https://pkg.go.dev/encoding/json/v2</a></p></li></ul><div><hr></div><p>To go deeper into production-grade service design with Go&#8212;including gRPC, Protocol Buffers, Kubernetes, and distributed system patterns check out Alexander Shuiskov&#8217;s updated book, <em><strong><a href="https://www.packtpub.com/en-us/product/microservices-with-go-9781836207320">Microservices with Go, Second Edition</a></strong></em>. It covers everything from service scaffolding and CI/CD pipelines to observability, secure communication, and advanced reliability techniques.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.packtpub.com/en-us/product/microservices-with-go-9781836207320" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UXOC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf837396-6c49-409d-86e8-e35105becb5f_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!UXOC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf837396-6c49-409d-86e8-e35105becb5f_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!UXOC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf837396-6c49-409d-86e8-e35105becb5f_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!UXOC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf837396-6c49-409d-86e8-e35105becb5f_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UXOC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf837396-6c49-409d-86e8-e35105becb5f_2250x2775" width="350" height="431.7307692307692" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bf837396-6c49-409d-86e8-e35105becb5f_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:350,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Microservices with Go&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.packtpub.com/en-us/product/microservices-with-go-9781836207320&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Microservices with Go" title="Microservices with Go" srcset="https://substackcdn.com/image/fetch/$s_!UXOC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf837396-6c49-409d-86e8-e35105becb5f_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!UXOC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf837396-6c49-409d-86e8-e35105becb5f_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!UXOC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf837396-6c49-409d-86e8-e35105becb5f_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!UXOC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf837396-6c49-409d-86e8-e35105becb5f_2250x2775 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here is what some reader&#8217;s have said:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FHqm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F703b4e86-3755-4fd4-bdbd-0623956bfa3c_867x432.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FHqm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F703b4e86-3755-4fd4-bdbd-0623956bfa3c_867x432.png 424w, https://substackcdn.com/image/fetch/$s_!FHqm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F703b4e86-3755-4fd4-bdbd-0623956bfa3c_867x432.png 848w, https://substackcdn.com/image/fetch/$s_!FHqm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F703b4e86-3755-4fd4-bdbd-0623956bfa3c_867x432.png 1272w, https://substackcdn.com/image/fetch/$s_!FHqm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F703b4e86-3755-4fd4-bdbd-0623956bfa3c_867x432.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FHqm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F703b4e86-3755-4fd4-bdbd-0623956bfa3c_867x432.png" width="867" height="432" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/703b4e86-3755-4fd4-bdbd-0623956bfa3c_867x432.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:432,&quot;width&quot;:867,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:93949,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/169634841?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F703b4e86-3755-4fd4-bdbd-0623956bfa3c_867x432.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FHqm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F703b4e86-3755-4fd4-bdbd-0623956bfa3c_867x432.png 424w, https://substackcdn.com/image/fetch/$s_!FHqm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F703b4e86-3755-4fd4-bdbd-0623956bfa3c_867x432.png 848w, https://substackcdn.com/image/fetch/$s_!FHqm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F703b4e86-3755-4fd4-bdbd-0623956bfa3c_867x432.png 1272w, https://substackcdn.com/image/fetch/$s_!FHqm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F703b4e86-3755-4fd4-bdbd-0623956bfa3c_867x432.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!arR0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12d25b0c-acf2-4062-996f-0b68a9891d58_863x205.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!arR0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12d25b0c-acf2-4062-996f-0b68a9891d58_863x205.png 424w, https://substackcdn.com/image/fetch/$s_!arR0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12d25b0c-acf2-4062-996f-0b68a9891d58_863x205.png 848w, https://substackcdn.com/image/fetch/$s_!arR0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12d25b0c-acf2-4062-996f-0b68a9891d58_863x205.png 1272w, https://substackcdn.com/image/fetch/$s_!arR0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12d25b0c-acf2-4062-996f-0b68a9891d58_863x205.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!arR0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12d25b0c-acf2-4062-996f-0b68a9891d58_863x205.png" width="863" height="205" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/12d25b0c-acf2-4062-996f-0b68a9891d58_863x205.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:205,&quot;width&quot;:863,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:46650,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/169634841?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12d25b0c-acf2-4062-996f-0b68a9891d58_863x205.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!arR0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12d25b0c-acf2-4062-996f-0b68a9891d58_863x205.png 424w, https://substackcdn.com/image/fetch/$s_!arR0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12d25b0c-acf2-4062-996f-0b68a9891d58_863x205.png 848w, https://substackcdn.com/image/fetch/$s_!arR0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12d25b0c-acf2-4062-996f-0b68a9891d58_863x205.png 1272w, https://substackcdn.com/image/fetch/$s_!arR0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12d25b0c-acf2-4062-996f-0b68a9891d58_863x205.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p>]]></content:encoded></item><item><title><![CDATA[Understanding DevOps Principles and CI/CD]]></title><description><![CDATA[The complete &#8220;Chapter 8: Understanding DevOps Principles and CI/CD&#8221; from the book Software Architecture with C# 12 and .NET 8 by Gabriel Baptista and Francesco Abbruzzese (Packt, February 2024).]]></description><link>https://deepengineering.substack.com/p/understanding-devops-principles-and</link><guid isPermaLink="false">https://deepengineering.substack.com/p/understanding-devops-principles-and</guid><dc:creator><![CDATA[Gabriel Lara Baptista]]></dc:creator><pubDate>Wed, 09 Jul 2025 08:15:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!H6Xh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b3d7820-b5be-4b43-b13d-1f506bbaa3f1_808x354.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Although many people define DevOps as a process, the more you work with it, the better you understand it as a philosophy. This chapter will cover the main concepts, principles, and tools you need to develop and deliver your software with DevOps.</p><p>By considering the DevOps philosophy, this chapter will focus on service design thinking, that is, keeping in mind that the software you design is a service offered to an organization or part of an organization. The main takeaway of this approach is that the highest priority is the value your software gives to the target organization. Moreover, you are not just offering working code and an agreement to fix bugs but also a solution for all the needs that your software was conceived for. In other words, your job includes everything it needs to satisfy those needs, such as monitoring users&#8217; satisfaction and quickly adapting the software when the user needs change, due to issues or new requirements.</p><p>Service design thinking is strictly tied to the Software as a Service (SaaS) model, which is discussed in <em>Chapter 10</em>, <em>Deciding on the Best Cloud-Based Solution</em>. In fact, the simplest way to offer solutions based on web services is to offer the usage of web services as a service, instead of selling the software that implements them.</p><p>Continuous Integration (CI) and Continuous Delivery (CD) are sometimes stated as prerequisites for DevOps. So, the purpose of this chapter is also to discuss how to enable CI/CD in a real scenario, considering the challenges that you, as a software architect, will need to deal with.</p><p>The following topics will be covered in this chapter:</p><ul><li><p>Understanding DevOps principles: CI, CD, and continuous feedback</p></li><li><p>Understanding how to implement DevOps using Azure DevOps and GitHub</p></li><li><p>Understanding the risks and challenges when using CI/CD</p></li></ul><p>The <code>WWTravelClub</code> project case, presented in <em>Chapter 21, Case Study</em>, will be discussed during these topics, giving you the opportunity to understand how the DevOps philosophy can be implemented. All the screenshots exemplifying the DevOps principles come from the main use case of the book, so you will be able to understand the DevOps principles easily.</p><p>By the end of this chapter, you will be able to design software according to service design thinking principles and use Azure Pipelines to deploy your application. You will be able to decide whether to use CI/CD in your project environment. Additionally, you will be able to define the tools needed for the successful use of this approach.</p><h1>Technical requirements</h1><p>This chapter requires Visual Studio 2022 Community Edition or better, with all the Azure tools installed. You may also need an Azure DevOps account, as described in <em>Chapter 3</em>, <em>Managing Requirements</em>. It requires a free Azure account too. If you have not already created one, the <em>Creating an Azure account</em> subsection of <em>Chapter 1</em>, <em>Understanding the Importance of Software Architecture</em>, explains how to do so. This chapter uses the same code as <em>Chapter 9</em>, <em>Testing Your Enterprise Application</em>.</p><h1>Describing DevOps</h1><p>DevOps is a term that is derived from the combination of the words <em>Development</em> and <em>Operations</em>, and the DevOps process simply unifies actions in these two areas. However, when you start to study a little bit more about it, you will realize that just connecting these two areas is not enough to achieve the true goals of this philosophy.</p><p>We can also say that DevOps is the process that answers the current needs of people, regarding software delivery.</p><p>Donovan Brown has a spectacular definition of what DevOps is: <em>DevOps is the union of people, process, and products to enable continuous delivery of value to our end users</em> (<a href="http://donovanbrown.com/post/what-is-devops">http://donovanbrown.com/post/what-is-devops</a>).</p><p>A way to deliver value continuously to our end users, using processes, people, and products: this is the best description of the DevOps philosophy. We need to develop and deliver customer-oriented software. As soon as all areas of a company understand that the key point is the end user, your task as a software architect is to present the technology that will facilitate the process of delivery.</p><p>All the content in this book is connected to this approach. It is never a matter of knowing a bunch of tools and technologies. As a software architect, you must understand that there is always a way to bring faster solutions to your end user easily, linked to their real needs. For this reason, you need to learn the DevOps principles, which will be discussed in this chapter.</p><h1>Understanding DevOps principles</h1><p>Considering DevOps as a philosophy, there are some principles that enable the process to work well in your team. These principles are CI, CD, and continuous feedback.</p><p>Microsoft has a specific web page for defining the DevOps overview, culture, practices, tools, and its relation to the cloud. Please check this out at <a href="https://azure.microsoft.com/en-us/overview/what-is-devops/">https://azure.microsoft.com/en-us/overview/what-is-devops/</a>.</p><p>DevOps is represented by the symbol of infinity in many books and technical articles. This represents the necessity for a continuous approach in the software development life cycle. During the cycle, you will need to plan, build, continuously integrate, deploy, operate, and get feedback, and then start all over again. The process must be a collaborative one since everybody has the same focus&#8212;to deliver value to the end user. Together with these principles, you &#8220;as a software architect&#8221; will need to decide on the best software development process that fits this approach. We discussed these processes in <em>Chapter 1</em>, <em>Understanding the Importance of Software Architecture</em>.</p><h2>CI</h2><p>When you start building enterprise solutions, collaboration is the key to getting things done faster and meeting the users&#8217; needs. Version control systems, as we discussed in <em>Chapter 4</em>, <em>Best Practices in Coding C# 12</em>, are essential for this process, but these tools do not do the job by themselves, especially if they are not well configured.</p><p>As a software architect, you must know that CI will help you to adopt a concrete approach to software development collaboration. When you implement it, as soon as a developer commits their code, the main code is automatically compiled and tested, according to unit and functional tests available in the project.</p><p>The good thing when you apply CI is that you can motivate developers to merge their changes as fast as they can to minimize merge conflicts. They can also share unit tests, which will improve the quality of software. This will make your master branch stable and safe after every commit from your team.</p><p>The key point of CI is the ability to identify problems faster. You will have this opportunity when you allow code to be tested and analyzed by others. The only thing the DevOps approach helps with is making sure this happens as quickly as possible.</p><h2>CD</h2><p>Once every single commit of your application is built and this code is tested with both unit tests and functional tests, you may also want to enable CD. Doing this is not just a matter of configuring the tool. As a software architect, you need to be sure that the team and the process are ready to go to this step.</p><p>The CD approach needs to guarantee that the production environment will be kept safe in each new deployment. To do so, a multi-stage pipeline is a great practice to be adopted.</p><p>The following screenshot shows an approach with common stages, using this book&#8217;s use case, WWTravelClub, as a demonstration:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MQbh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7be87b14-8d21-48e5-83c2-05fbe25cca11_875x302.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MQbh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7be87b14-8d21-48e5-83c2-05fbe25cca11_875x302.png 424w, https://substackcdn.com/image/fetch/$s_!MQbh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7be87b14-8d21-48e5-83c2-05fbe25cca11_875x302.png 848w, https://substackcdn.com/image/fetch/$s_!MQbh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7be87b14-8d21-48e5-83c2-05fbe25cca11_875x302.png 1272w, https://substackcdn.com/image/fetch/$s_!MQbh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7be87b14-8d21-48e5-83c2-05fbe25cca11_875x302.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MQbh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7be87b14-8d21-48e5-83c2-05fbe25cca11_875x302.png" width="875" height="302" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7be87b14-8d21-48e5-83c2-05fbe25cca11_875x302.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:302,&quot;width&quot;:875,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A screenshot of a computer  Description automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A screenshot of a computer  Description automatically generated" title="A screenshot of a computer  Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!MQbh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7be87b14-8d21-48e5-83c2-05fbe25cca11_875x302.png 424w, https://substackcdn.com/image/fetch/$s_!MQbh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7be87b14-8d21-48e5-83c2-05fbe25cca11_875x302.png 848w, https://substackcdn.com/image/fetch/$s_!MQbh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7be87b14-8d21-48e5-83c2-05fbe25cca11_875x302.png 1272w, https://substackcdn.com/image/fetch/$s_!MQbh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7be87b14-8d21-48e5-83c2-05fbe25cca11_875x302.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.1: Release stages using Azure DevOps</em></p><p>As you can see, these stages were configured using the Azure DevOps release pipeline, which will be explained soon. Each stage has its own purpose, which will affect the quality of the product ultimately delivered. Let&#8217;s look at the stages:</p><ul><li><p>Development/tests: This stage is used by developers and testers to build new functionality. This environment will certainly be the one that is most exposed to bugs and incomplete functions.</p></li><li><p>Staging: This environment gives a brief version of new functionalities to areas of the team not related to development and tests. Program managers, marketing, vendors, and others can use it as an area of study, validation, and even preproduction. Additionally, quality assurance teams can guarantee that the new releases are correctly deployed, considering both functionality and infrastructure.</p></li><li><p>Production: This is the stage where customers have their solution running. The goal for a good production environment, according to CD, is to have it updated as quickly as possible. The frequency will vary according to team size, but there are some approaches where this process happens more than once a day.</p></li></ul><p>The adoption of these three stages of deploying your application will positively impact the quality of the solution. It will also enable the team to have a safer deployment process, with fewer risks and better product stability. This approach may look a bit expensive at first sight, but without it, the results of bad deployment will generally be more expensive than this investment.</p><h2>Risks and challenges when using CI/CD</h2><p>Now that we have an idea of how useful CI/CD is, it would be nice to think about the risks and challenges you may encounter while implementing it. The goal of this section is to help you, as a software architect, mitigate the risks and find a better way to overcome the challenges, using good processes and techniques.</p><p>The list of risks and challenges that will be discussed in this section are as follows:</p><ul><li><p>Continuous production deployment</p></li><li><p>Incomplete features in production</p></li><li><p>Unstable solutions for testing</p></li></ul><p>Once you have the techniques and the processes defined to deal with them, there is no reason not to use CI/CD.</p><p>Remember that DevOps does not depend on CI/CD. You can use a process where code integration and software deployment are human-based. However, CI/CD does make DevOps work more smoothly.</p><p>Now, let us have a look at them.</p><h3>Disabling continuous production deployment</h3><p>Continuous production deployment is a process where, after a commit of a new piece of code and some pipeline steps, you will have this code in the production environment. This is not impossible, but it is hard and expensive to do. Besides, you need to have well-established, sophisticated processes, as well as a team with substantial experience and expertise to enable it. The problem is that most of the demos and samples you will find on the internet presenting CI/CD will show you a fast track to deploying code. The demonstrations of CI/CD make it look so simple! This <em>simplicity</em> might suggest that you should implement it as soon as possible. However, if you think a little more, this scenario can be dangerous if you deploy directly into production! In a solution that needs to be available 24 hours a day, 7 days a week, this is impractical. So you will need to worry about that and think of different solutions.</p><p>The first one is the use of a multi-stage scenario, as we described before. The multi-stage scenario can bring more security to the ecosystem of the deployment you are building. Besides, you will get more options to avoid incorrect deployments into production, such as pre-deployment approvals,</p><p>You can build a deployment pipeline where all your code and software structure will be updated by this tool. However, if you have something outside of this scenario, such as database scripts and environment configurations, an incorrect publication into production may cause damage to end users. Besides, the decision of when the production environment will be updated needs to be planned, and in many scenarios, all the platform users need to be notified of the upcoming change. It is worth mentioning that, in these hard-to-decide cases, it is a good idea to use a change management procedure, based on the Information Technology Infrastructure Library (ITIL) or ISO 20000.</p><p>So the challenge of delivering code to production will make you think about a schedule to do so. It does not matter whether your cycle is monthly, daily, or even at each commit. The key point here is that you need to create a process and a pipeline that guarantees that only good and approved software is at the production stage. It is worth noting, however, that the longer you leave deployments, the scarier they are, as the deviation between the previously deployed version and the new one will be greater, and more changes will be pushed out in one go. The more frequently you can deploy, the better.</p><h3>Incomplete features</h3><p>While a developer of your team is creating a new feature or fixing a bug, you will probably consider generating a branch, which means they can avoid the use of the branch designed for CD. A branch can be considered a feature available in code repositories to enable the creation of an independent line of development, since it isolates code.</p><p>As you can see in the following screenshot, creating a branch for wwtravelclub using Visual Studio is quite simple:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BomA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406a0f00-d005-4460-a547-f8854047f568_633x589.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BomA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406a0f00-d005-4460-a547-f8854047f568_633x589.png 424w, https://substackcdn.com/image/fetch/$s_!BomA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406a0f00-d005-4460-a547-f8854047f568_633x589.png 848w, https://substackcdn.com/image/fetch/$s_!BomA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406a0f00-d005-4460-a547-f8854047f568_633x589.png 1272w, https://substackcdn.com/image/fetch/$s_!BomA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406a0f00-d005-4460-a547-f8854047f568_633x589.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BomA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406a0f00-d005-4460-a547-f8854047f568_633x589.png" width="633" height="589" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/406a0f00-d005-4460-a547-f8854047f568_633x589.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:589,&quot;width&quot;:633,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Graphical user interface, text, application, email  Description automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Graphical user interface, text, application, email  Description automatically generated" title="Graphical user interface, text, application, email  Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!BomA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406a0f00-d005-4460-a547-f8854047f568_633x589.png 424w, https://substackcdn.com/image/fetch/$s_!BomA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406a0f00-d005-4460-a547-f8854047f568_633x589.png 848w, https://substackcdn.com/image/fetch/$s_!BomA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406a0f00-d005-4460-a547-f8854047f568_633x589.png 1272w, https://substackcdn.com/image/fetch/$s_!BomA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F406a0f00-d005-4460-a547-f8854047f568_633x589.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.2: Creating a branch in Visual Studio</em></p><p>This seems to be a good approach, but let us suppose that the developer has considered the implementation ready for deployment and has just merged the code into the master branch, although this is also considered a bad practice. What if this feature is not ready yet, just because a requirement was omitted? What if the bug has caused incorrect behavior? The result could be a release with an incomplete feature or an incorrect fix.</p><p>One of the good practices to avoid broken features and even incorrect fixes in the master branch is the use of Pull Requests (PRs). PRs will let other team developers know that the code you developed is ready to be merged. The following screenshot shows how you can use the Azure DevOps WWTravelClub repository to create a New pull request for a change you have made.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3o1O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368798da-b4a5-41ee-bebc-53258041e037_875x914.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3o1O!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368798da-b4a5-41ee-bebc-53258041e037_875x914.png 424w, https://substackcdn.com/image/fetch/$s_!3o1O!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368798da-b4a5-41ee-bebc-53258041e037_875x914.png 848w, https://substackcdn.com/image/fetch/$s_!3o1O!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368798da-b4a5-41ee-bebc-53258041e037_875x914.png 1272w, https://substackcdn.com/image/fetch/$s_!3o1O!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368798da-b4a5-41ee-bebc-53258041e037_875x914.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3o1O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368798da-b4a5-41ee-bebc-53258041e037_875x914.png" width="875" height="914" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/368798da-b4a5-41ee-bebc-53258041e037_875x914.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:914,&quot;width&quot;:875,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Interface gr&#225;fica do usu&#225;rio, Texto, Aplicativo, Email  Descri&#231;&#227;o gerada automaticamente&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Interface gr&#225;fica do usu&#225;rio, Texto, Aplicativo, Email  Descri&#231;&#227;o gerada automaticamente" title="Interface gr&#225;fica do usu&#225;rio, Texto, Aplicativo, Email  Descri&#231;&#227;o gerada automaticamente" srcset="https://substackcdn.com/image/fetch/$s_!3o1O!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368798da-b4a5-41ee-bebc-53258041e037_875x914.png 424w, https://substackcdn.com/image/fetch/$s_!3o1O!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368798da-b4a5-41ee-bebc-53258041e037_875x914.png 848w, https://substackcdn.com/image/fetch/$s_!3o1O!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368798da-b4a5-41ee-bebc-53258041e037_875x914.png 1272w, https://substackcdn.com/image/fetch/$s_!3o1O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368798da-b4a5-41ee-bebc-53258041e037_875x914.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.3: Creating a PR</em></p><p>Once the PR is created and the reviewers are defined, each reviewer will be able to analyze the code and decide whether it is healthy enough to be in the master branch.</p><p>The following screenshot shows a way to check the code, by using the compare tool to analyze the change in the WWTravelClub code:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hYo0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73822553-1bfd-433f-9746-ec0222ff43b4_604x464.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hYo0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73822553-1bfd-433f-9746-ec0222ff43b4_604x464.png 424w, https://substackcdn.com/image/fetch/$s_!hYo0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73822553-1bfd-433f-9746-ec0222ff43b4_604x464.png 848w, https://substackcdn.com/image/fetch/$s_!hYo0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73822553-1bfd-433f-9746-ec0222ff43b4_604x464.png 1272w, https://substackcdn.com/image/fetch/$s_!hYo0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73822553-1bfd-433f-9746-ec0222ff43b4_604x464.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hYo0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73822553-1bfd-433f-9746-ec0222ff43b4_604x464.png" width="604" height="464" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/73822553-1bfd-433f-9746-ec0222ff43b4_604x464.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:464,&quot;width&quot;:604,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Interface gr&#225;fica do usu&#225;rio, Texto, Aplicativo, Email  Descri&#231;&#227;o gerada automaticamente&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Interface gr&#225;fica do usu&#225;rio, Texto, Aplicativo, Email  Descri&#231;&#227;o gerada automaticamente" title="Interface gr&#225;fica do usu&#225;rio, Texto, Aplicativo, Email  Descri&#231;&#227;o gerada automaticamente" srcset="https://substackcdn.com/image/fetch/$s_!hYo0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73822553-1bfd-433f-9746-ec0222ff43b4_604x464.png 424w, https://substackcdn.com/image/fetch/$s_!hYo0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73822553-1bfd-433f-9746-ec0222ff43b4_604x464.png 848w, https://substackcdn.com/image/fetch/$s_!hYo0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73822553-1bfd-433f-9746-ec0222ff43b4_604x464.png 1272w, https://substackcdn.com/image/fetch/$s_!hYo0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73822553-1bfd-433f-9746-ec0222ff43b4_604x464.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.4: Analyzing the PR</em></p><p>Once all approvals are done, you will be able to safely merge the code to the master branch, as you can see in the following screenshot. To merge the code, you will need to click on Complete merge. It is important to mention that you can also do this in Visual Studio, which has a better user interface. If the CI trigger is enabled in the WWTravelClub project, as we will show in this chapter, Azure DevOps will start a build pipeline:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KsI3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba471d2-6928-433e-b5a9-18f1061da255_549x412.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KsI3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba471d2-6928-433e-b5a9-18f1061da255_549x412.png 424w, https://substackcdn.com/image/fetch/$s_!KsI3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba471d2-6928-433e-b5a9-18f1061da255_549x412.png 848w, https://substackcdn.com/image/fetch/$s_!KsI3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba471d2-6928-433e-b5a9-18f1061da255_549x412.png 1272w, https://substackcdn.com/image/fetch/$s_!KsI3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba471d2-6928-433e-b5a9-18f1061da255_549x412.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KsI3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba471d2-6928-433e-b5a9-18f1061da255_549x412.png" width="549" height="412" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dba471d2-6928-433e-b5a9-18f1061da255_549x412.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:412,&quot;width&quot;:549,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Interface gr&#225;fica do usu&#225;rio, Texto, Aplicativo, Email  Descri&#231;&#227;o gerada automaticamente&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Interface gr&#225;fica do usu&#225;rio, Texto, Aplicativo, Email  Descri&#231;&#227;o gerada automaticamente" title="Interface gr&#225;fica do usu&#225;rio, Texto, Aplicativo, Email  Descri&#231;&#227;o gerada automaticamente" srcset="https://substackcdn.com/image/fetch/$s_!KsI3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba471d2-6928-433e-b5a9-18f1061da255_549x412.png 424w, https://substackcdn.com/image/fetch/$s_!KsI3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba471d2-6928-433e-b5a9-18f1061da255_549x412.png 848w, https://substackcdn.com/image/fetch/$s_!KsI3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba471d2-6928-433e-b5a9-18f1061da255_549x412.png 1272w, https://substackcdn.com/image/fetch/$s_!KsI3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba471d2-6928-433e-b5a9-18f1061da255_549x412.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.5: Merging the PR</em></p><p>Without a process like this, there is a great chance of having a master branch with a lot of bad code, and this code being deployed there may cause damage together with CD. A code review is an excellent practice in CI/CD scenarios, and it is considered a wonderful practice to create good quality software in general.</p><p>The challenge that you need to focus on here is guaranteeing that only entire features will appear to your end users. You can use the feature flag principle to solve this, which is a technique that makes sure only features that are ready are presented to end users.</p><p>In the Feature Flag or Feature Toggle technique, you must create a solution that has, in each feature, the possibility to test it in a setup to see whether it is enabled or not. According to this, all the functionalities will be presented to the user.</p><p>It is worth mentioning that to control feature availability in an environment, feature flags are much safer than using branching/PRs. Both have their place, but PRs are about controlling the quality of code at the CI stage, and feature flags are for controlling feature availability at the CD stage.</p><p>Again, we are not talking about CI/CD as a <em>tool</em> but as a <em>process</em>, to be defined and used every single time you need to deliver code for production.</p><h3>An unstable solution for testing</h3><p>If you have already mitigated the two other risks presented in this section, you may find it uncommon to have bad code after CI/CD. It is true that the worries presented earlier will certainly reduce if you work with a multi-stage scenario and PRs before pushing to the final stage. But the risk of having unstable code, especially when it comes to business logic rules, remains even if you apply all the recommendations we will discuss later.</p><p>But is there a way to accelerate the evaluation of a release while being sure that this new release is ready for your stakeholders&#8217; tests? Yes, there is! Technically, the way you can do so is by automating unit and functional testing. This technique is explained in more detail in <em>Chapter 9</em>, <em>Testing Your Enterprise Application</em>.</p><p>However, it is worth saying that it is impracticable to automate every single part of a piece of software, considering the efforts needed to do so. Besides, the maintenance of automation can be more expensive in scenarios where the user interface or the business rules change a lot. Although this is a tough decision, as a software architect, you must always incentivize the usage of automated testing.</p><p>To exemplify it, let us have a look at the following screenshot, which shows the unit and functional test samples for WWTravelClub, created by an Azure DevOps project template:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zOHz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba443f8-eec9-44fe-a8bd-368b7b1610ce_874x614.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zOHz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba443f8-eec9-44fe-a8bd-368b7b1610ce_874x614.png 424w, https://substackcdn.com/image/fetch/$s_!zOHz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba443f8-eec9-44fe-a8bd-368b7b1610ce_874x614.png 848w, https://substackcdn.com/image/fetch/$s_!zOHz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba443f8-eec9-44fe-a8bd-368b7b1610ce_874x614.png 1272w, https://substackcdn.com/image/fetch/$s_!zOHz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba443f8-eec9-44fe-a8bd-368b7b1610ce_874x614.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zOHz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba443f8-eec9-44fe-a8bd-368b7b1610ce_874x614.png" width="874" height="614" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eba443f8-eec9-44fe-a8bd-368b7b1610ce_874x614.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:614,&quot;width&quot;:874,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Graphical user interface, text, application, email  Description automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Graphical user interface, text, application, email  Description automatically generated" title="Graphical user interface, text, application, email  Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!zOHz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba443f8-eec9-44fe-a8bd-368b7b1610ce_874x614.png 424w, https://substackcdn.com/image/fetch/$s_!zOHz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba443f8-eec9-44fe-a8bd-368b7b1610ce_874x614.png 848w, https://substackcdn.com/image/fetch/$s_!zOHz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba443f8-eec9-44fe-a8bd-368b7b1610ce_874x614.png 1272w, https://substackcdn.com/image/fetch/$s_!zOHz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feba443f8-eec9-44fe-a8bd-368b7b1610ce_874x614.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.6: Unit and functional tests projects</em></p><p>There are some architectural patterns, such as SOLID, presented in <em>Chapter 6</em>, and quality assurance approaches, such as peer review, that will give you better results than software testing. However, these approaches do not invalidate automation practice. The truth is that all of them will be useful for getting a stable solution, especially when you run a CI scenario. In this environment, the best thing you can do is to detect errors and incorrect behaviors as fast as you can. Both unit and functional tests, as shown earlier, will help you with this.</p><p>Unit tests will help you a lot in discovering business logic errors before deployment, during the build pipeline. For instance, in the following screenshot from the WWTravelClub build process, you will find a simulated error that canceled the build, since the unit test did not pass:</p><p>Certain free services on Azure may be deactivated due to the possibility of misuse. However, you have the option to request the reactivation of these services by submitting a request.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SGaN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2645d87a-eb92-439b-b206-503ab682f7ae_877x516.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SGaN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2645d87a-eb92-439b-b206-503ab682f7ae_877x516.png 424w, https://substackcdn.com/image/fetch/$s_!SGaN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2645d87a-eb92-439b-b206-503ab682f7ae_877x516.png 848w, https://substackcdn.com/image/fetch/$s_!SGaN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2645d87a-eb92-439b-b206-503ab682f7ae_877x516.png 1272w, https://substackcdn.com/image/fetch/$s_!SGaN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2645d87a-eb92-439b-b206-503ab682f7ae_877x516.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SGaN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2645d87a-eb92-439b-b206-503ab682f7ae_877x516.png" width="877" height="516" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2645d87a-eb92-439b-b206-503ab682f7ae_877x516.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:516,&quot;width&quot;:877,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!SGaN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2645d87a-eb92-439b-b206-503ab682f7ae_877x516.png 424w, https://substackcdn.com/image/fetch/$s_!SGaN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2645d87a-eb92-439b-b206-503ab682f7ae_877x516.png 848w, https://substackcdn.com/image/fetch/$s_!SGaN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2645d87a-eb92-439b-b206-503ab682f7ae_877x516.png 1272w, https://substackcdn.com/image/fetch/$s_!SGaN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2645d87a-eb92-439b-b206-503ab682f7ae_877x516.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.7: Unit test result</em></p><p>The way to get this error is quite simple. You need to code something that does not respond according to what the unit tests are checking. Once you commit it, assuming you have the trigger for CD on, you will build the code in the pipeline. So after the build of the code, the unit tests will run. If the code does not match the tests anymore, you will get an error.</p><p>The following screenshot shows an error during the functional tests in the Development/Tests stage of the WWTravelClub project. In this instance, the Development/Tests environment has a bug that was rapidly detected by functional tests:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9VaY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67582e61-d0b5-4582-b079-fe85db2ea483_877x487.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9VaY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67582e61-d0b5-4582-b079-fe85db2ea483_877x487.png 424w, https://substackcdn.com/image/fetch/$s_!9VaY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67582e61-d0b5-4582-b079-fe85db2ea483_877x487.png 848w, https://substackcdn.com/image/fetch/$s_!9VaY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67582e61-d0b5-4582-b079-fe85db2ea483_877x487.png 1272w, https://substackcdn.com/image/fetch/$s_!9VaY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67582e61-d0b5-4582-b079-fe85db2ea483_877x487.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9VaY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67582e61-d0b5-4582-b079-fe85db2ea483_877x487.png" width="877" height="487" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/67582e61-d0b5-4582-b079-fe85db2ea483_877x487.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:487,&quot;width&quot;:877,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!9VaY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67582e61-d0b5-4582-b079-fe85db2ea483_877x487.png 424w, https://substackcdn.com/image/fetch/$s_!9VaY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67582e61-d0b5-4582-b079-fe85db2ea483_877x487.png 848w, https://substackcdn.com/image/fetch/$s_!9VaY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67582e61-d0b5-4582-b079-fe85db2ea483_877x487.png 1272w, https://substackcdn.com/image/fetch/$s_!9VaY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67582e61-d0b5-4582-b079-fe85db2ea483_877x487.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.8: Functional test result</em></p><p>But this is not the only good thing about applying functional tests during CI/CD. Let us look at the following screenshot from the Releases pipeline interface in Azure DevOps. If you look at Release-9, you will realize that, since this error happened after the publication in the Development/Tests environment, the multi-stage environment will protect the other stages of the wrong deployment, especially the WWTravelClub production stage:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Xt0E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98c69de0-f74b-4920-adfb-322d15815c5e_876x324.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xt0E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98c69de0-f74b-4920-adfb-322d15815c5e_876x324.png 424w, https://substackcdn.com/image/fetch/$s_!Xt0E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98c69de0-f74b-4920-adfb-322d15815c5e_876x324.png 848w, https://substackcdn.com/image/fetch/$s_!Xt0E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98c69de0-f74b-4920-adfb-322d15815c5e_876x324.png 1272w, https://substackcdn.com/image/fetch/$s_!Xt0E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98c69de0-f74b-4920-adfb-322d15815c5e_876x324.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xt0E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98c69de0-f74b-4920-adfb-322d15815c5e_876x324.png" width="876" height="324" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/98c69de0-f74b-4920-adfb-322d15815c5e_876x324.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:324,&quot;width&quot;:876,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Graphical user interface, text, application, email  Description automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Graphical user interface, text, application, email  Description automatically generated" title="Graphical user interface, text, application, email  Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!Xt0E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98c69de0-f74b-4920-adfb-322d15815c5e_876x324.png 424w, https://substackcdn.com/image/fetch/$s_!Xt0E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98c69de0-f74b-4920-adfb-322d15815c5e_876x324.png 848w, https://substackcdn.com/image/fetch/$s_!Xt0E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98c69de0-f74b-4920-adfb-322d15815c5e_876x324.png 1272w, https://substackcdn.com/image/fetch/$s_!Xt0E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98c69de0-f74b-4920-adfb-322d15815c5e_876x324.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.9: Multi-stage environment protection</em></p><p>The key point for success in the CI process is to think about it as a useful tool to accelerate the delivery of software and to not forget that a team always needs to deliver value to its end users. With this approach, the techniques presented earlier will provide incredible ways to achieve the results that your team aims for.</p><h2>Continuous feedback</h2><p>Once you have a solution that runs perfectly in the deployment scenario described in the previous section, feedback will be essential for your team to understand the results of the release and how the version works for customers. To get this feedback, there are some tools that can help both developers and customers, bringing them together to fast-track the process of feedback.</p><p>The main purpose of continuous feedback is to enable developers to get information about the application running in production, enabling the team to improve the infrastructure of the environment deployed and, at the same time, detect improvements that can be made in the source code and user interface.</p><h1>Tools to facilitate DevOps implementation</h1><p>Considering that DevOps is a philosophy, there are many tools that can be used to help you with its implementation. The following topics will present some of the most-used ones in a Microsoft environment.</p><h2>Azure DevOps</h2><p>As soon as you start working with a platform such as Azure DevOps, enabling CI/CD will be easy when it comes to clicking on the options to do so. So technology is not the Achilles&#8217; heel for implementing this process.</p><p>The following screenshot shows an example of how easy it is to turn on CI/CD using the Azure DevOps WWTravelClub pipeline. By clicking in the Pipelines and editing it, you will be able to set a trigger that enables CI/CD after some clicks:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gmBO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc318bd06-9ede-420e-b6d6-e829a628549b_876x387.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gmBO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc318bd06-9ede-420e-b6d6-e829a628549b_876x387.png 424w, https://substackcdn.com/image/fetch/$s_!gmBO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc318bd06-9ede-420e-b6d6-e829a628549b_876x387.png 848w, https://substackcdn.com/image/fetch/$s_!gmBO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc318bd06-9ede-420e-b6d6-e829a628549b_876x387.png 1272w, https://substackcdn.com/image/fetch/$s_!gmBO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc318bd06-9ede-420e-b6d6-e829a628549b_876x387.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gmBO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc318bd06-9ede-420e-b6d6-e829a628549b_876x387.png" width="876" height="387" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c318bd06-9ede-420e-b6d6-e829a628549b_876x387.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:387,&quot;width&quot;:876,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A screenshot of a computer  Description automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A screenshot of a computer  Description automatically generated" title="A screenshot of a computer  Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!gmBO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc318bd06-9ede-420e-b6d6-e829a628549b_876x387.png 424w, https://substackcdn.com/image/fetch/$s_!gmBO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc318bd06-9ede-420e-b6d6-e829a628549b_876x387.png 848w, https://substackcdn.com/image/fetch/$s_!gmBO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc318bd06-9ede-420e-b6d6-e829a628549b_876x387.png 1272w, https://substackcdn.com/image/fetch/$s_!gmBO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc318bd06-9ede-420e-b6d6-e829a628549b_876x387.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.10: Enabling the CI trigger</em></p><p>CI/CD will help you solve some problems. For instance, it will force you to test your code, since you will need to commit the changes faster so that other developers can make use of the code you are programming.</p><p>Conversely, you will not do CI/CD just by enabling a CI build in Azure DevOps. For sure, you will turn on the possibility of starting a build as soon as you get a commit done and the code is complete, but this is far from saying you have CI/CD available in your solution.</p><p>The reason you, as a software architect, need to worry a bit more about it is related to a real understanding of what DevOps is. The need to deliver value to the end user will always be a good way to decide how the development life cycle will work. So even if turning on CI/CD is easy, what is the real business impact of this feature being enabled for your end users? Once you have all the answers to this question and you know how to reduce the risks of its implementation, then you will be able to say that you have a CI/CD process implemented.</p><p>CI/CD is a principle that will make DevOps work better and faster. However, DevOps can live without it, if you are not sure if your process is mature enough to enable code to be continuously delivered.</p><p>Moreover, if you turn on CI/CD in a team that is not mature enough to deal with its complexity, you will probably cause a bad understanding of DevOps, since you will start incurring some risks while deploying your solution. The point is that CI/CD is not a prerequisite for DevOps.</p><p>When you have CI/CD enabled, you can make things faster in DevOps. However, you can practice DevOps without it.</p><p>Deployments and other release artifacts are added to different pipelines, called release pipelines, to decouple them from build-related artifacts. With Release Pipelines, you cannot edit a <code>.yaml</code> file, but you will work with a graphic interface, as follows:</p><ol><li><p>Click the Releases left menu tab to create a new Release Pipeline. As soon as you click Add a new pipeline, you will be prompted to add the first task of the first pipeline stage. In fact, the whole release pipeline is composed of different stages, each grouping sequences of tasks. While each stage is just a sequence of tasks, the stages diagram can branch, and we can add several branches after each stage. This way, we can deploy to different platforms that each require different tasks. In our simple example, we will use a single stage.</p></li><li><p>Select the Azure App Service Deployment task. As soon as you add this task, you will be prompted to fill in the missing information.</p></li><li><p>Select your subscription, and then, if an Authorization button appears, click it to authorize Azure Pipelines to access your subscription. Then, select Windows as the deployment platform, and finally, select the app service you created from the App service name drop-down list. Task settings are automatically saved while you write them, so you just need to click the Save button for the whole pipeline.</p></li><li><p>Now, we need to connect this pipeline to a source artifact. Click the Add Artifact button and then select Build as the source type, because we need to connect the new release pipeline with the ZIP file created by our build pipeline. A settings window appears:</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9W46!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe57194fc-291b-4875-bf44-7f2257cffed0_647x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9W46!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe57194fc-291b-4875-bf44-7f2257cffed0_647x572.png 424w, https://substackcdn.com/image/fetch/$s_!9W46!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe57194fc-291b-4875-bf44-7f2257cffed0_647x572.png 848w, https://substackcdn.com/image/fetch/$s_!9W46!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe57194fc-291b-4875-bf44-7f2257cffed0_647x572.png 1272w, https://substackcdn.com/image/fetch/$s_!9W46!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe57194fc-291b-4875-bf44-7f2257cffed0_647x572.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9W46!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe57194fc-291b-4875-bf44-7f2257cffed0_647x572.png" width="647" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e57194fc-291b-4875-bf44-7f2257cffed0_647x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:647,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!9W46!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe57194fc-291b-4875-bf44-7f2257cffed0_647x572.png 424w, https://substackcdn.com/image/fetch/$s_!9W46!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe57194fc-291b-4875-bf44-7f2257cffed0_647x572.png 848w, https://substackcdn.com/image/fetch/$s_!9W46!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe57194fc-291b-4875-bf44-7f2257cffed0_647x572.png 1272w, https://substackcdn.com/image/fetch/$s_!9W46!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe57194fc-291b-4875-bf44-7f2257cffed0_647x572.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.11: Defining the artifact to publish</em></p><ol start="5"><li><p>Select our previous build pipeline from the drop-down list, and keep Latest as the version. Accept the suggested name under Source alias.</p></li><li><p>Our release pipeline is ready and can be used as-is. The image of the source artifact you just added contains a trigger icon in its top-right corner, as follows:</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ArwT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e3cba1c-2590-452c-b249-7c6b510a972d_311x407.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ArwT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e3cba1c-2590-452c-b249-7c6b510a972d_311x407.png 424w, https://substackcdn.com/image/fetch/$s_!ArwT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e3cba1c-2590-452c-b249-7c6b510a972d_311x407.png 848w, https://substackcdn.com/image/fetch/$s_!ArwT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e3cba1c-2590-452c-b249-7c6b510a972d_311x407.png 1272w, https://substackcdn.com/image/fetch/$s_!ArwT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e3cba1c-2590-452c-b249-7c6b510a972d_311x407.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ArwT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e3cba1c-2590-452c-b249-7c6b510a972d_311x407.png" width="311" height="407" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0e3cba1c-2590-452c-b249-7c6b510a972d_311x407.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:407,&quot;width&quot;:311,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!ArwT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e3cba1c-2590-452c-b249-7c6b510a972d_311x407.png 424w, https://substackcdn.com/image/fetch/$s_!ArwT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e3cba1c-2590-452c-b249-7c6b510a972d_311x407.png 848w, https://substackcdn.com/image/fetch/$s_!ArwT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e3cba1c-2590-452c-b249-7c6b510a972d_311x407.png 1272w, https://substackcdn.com/image/fetch/$s_!ArwT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e3cba1c-2590-452c-b249-7c6b510a972d_311x407.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.12: Artifact ready to publish</em></p><ol start="7"><li><p>If you click on the trigger icon, you are given the option to automatically trigger the release pipeline as soon as a new build is available:</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1N6m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a82b124-14a7-442d-b5e2-c9a5d1ad14fe_809x331.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1N6m!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a82b124-14a7-442d-b5e2-c9a5d1ad14fe_809x331.png 424w, https://substackcdn.com/image/fetch/$s_!1N6m!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a82b124-14a7-442d-b5e2-c9a5d1ad14fe_809x331.png 848w, https://substackcdn.com/image/fetch/$s_!1N6m!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a82b124-14a7-442d-b5e2-c9a5d1ad14fe_809x331.png 1272w, https://substackcdn.com/image/fetch/$s_!1N6m!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a82b124-14a7-442d-b5e2-c9a5d1ad14fe_809x331.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1N6m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a82b124-14a7-442d-b5e2-c9a5d1ad14fe_809x331.png" width="809" height="331" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7a82b124-14a7-442d-b5e2-c9a5d1ad14fe_809x331.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:331,&quot;width&quot;:809,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Graphical user interface, application, Teams  Description automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Graphical user interface, application, Teams  Description automatically generated" title="Graphical user interface, application, Teams  Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!1N6m!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a82b124-14a7-442d-b5e2-c9a5d1ad14fe_809x331.png 424w, https://substackcdn.com/image/fetch/$s_!1N6m!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a82b124-14a7-442d-b5e2-c9a5d1ad14fe_809x331.png 848w, https://substackcdn.com/image/fetch/$s_!1N6m!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a82b124-14a7-442d-b5e2-c9a5d1ad14fe_809x331.png 1272w, https://substackcdn.com/image/fetch/$s_!1N6m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a82b124-14a7-442d-b5e2-c9a5d1ad14fe_809x331.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.13: Enabling the continuous deployment trigger</em></p><ol start="8"><li><p>Keep it disabled; we can enable it after we have completed and manually tested the release pipeline.</p></li></ol><p>As we mentioned earlier, in preparation for an automatic trigger, we need to add a human approval task before the application is deployed. Let&#8217;s add it with the following steps:</p><ol><li><p>Click the three dots on the right of the Stage 1 header:</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3h9l!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcb72c60-b10e-45cd-89e2-2984b83d498a_340x206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3h9l!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcb72c60-b10e-45cd-89e2-2984b83d498a_340x206.png 424w, https://substackcdn.com/image/fetch/$s_!3h9l!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcb72c60-b10e-45cd-89e2-2984b83d498a_340x206.png 848w, https://substackcdn.com/image/fetch/$s_!3h9l!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcb72c60-b10e-45cd-89e2-2984b83d498a_340x206.png 1272w, https://substackcdn.com/image/fetch/$s_!3h9l!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcb72c60-b10e-45cd-89e2-2984b83d498a_340x206.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3h9l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcb72c60-b10e-45cd-89e2-2984b83d498a_340x206.png" width="340" height="206" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bcb72c60-b10e-45cd-89e2-2984b83d498a_340x206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:206,&quot;width&quot;:340,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!3h9l!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcb72c60-b10e-45cd-89e2-2984b83d498a_340x206.png 424w, https://substackcdn.com/image/fetch/$s_!3h9l!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcb72c60-b10e-45cd-89e2-2984b83d498a_340x206.png 848w, https://substackcdn.com/image/fetch/$s_!3h9l!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcb72c60-b10e-45cd-89e2-2984b83d498a_340x206.png 1272w, https://substackcdn.com/image/fetch/$s_!3h9l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcb72c60-b10e-45cd-89e2-2984b83d498a_340x206.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 8.14: Adding human approval to a stage</em></p><ol start="2"><li><p>Then, select Add an agentless job. Once the agentless job has been added, click the Add button and add a Manual intervention task. The following screenshot shows the Manual intervention settings:</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vdTz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec4b5b84-f673-414d-9e03-9a9d292fd2f7_548x553.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vdTz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec4b5b84-f673-414d-9e03-9a9d292fd2f7_548x553.png 424w, https://substackcdn.com/image/fetch/$s_!vdTz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec4b5b84-f673-414d-9e03-9a9d292fd2f7_548x553.png 848w, https://substackcdn.com/image/fetch/$s_!vdTz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec4b5b84-f673-414d-9e03-9a9d292fd2f7_548x553.png 1272w, https://substackcdn.com/image/fetch/$s_!vdTz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec4b5b84-f673-414d-9e03-9a9d292fd2f7_548x553.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vdTz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec4b5b84-f673-414d-9e03-9a9d292fd2f7_548x553.png" width="548" height="553" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ec4b5b84-f673-414d-9e03-9a9d292fd2f7_548x553.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:553,&quot;width&quot;:548,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!vdTz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec4b5b84-f673-414d-9e03-9a9d292fd2f7_548x553.png 424w, https://substackcdn.com/image/fetch/$s_!vdTz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec4b5b84-f673-414d-9e03-9a9d292fd2f7_548x553.png 848w, https://substackcdn.com/image/fetch/$s_!vdTz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec4b5b84-f673-414d-9e03-9a9d292fd2f7_548x553.png 1272w, https://substackcdn.com/image/fetch/$s_!vdTz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec4b5b84-f673-414d-9e03-9a9d292fd2f7_548x553.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.15: Configuring human approval for a stage</em></p><p>Add instructions for the operator, and select your account in the Notify users field.</p><ol start="3"><li><p>Now, drag the whole Agentless job with the mouse, and place it before the application deployment task. It should look like this:</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GEVp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0f495e8-7bdc-43d7-bfbb-739030d46512_554x438.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GEVp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0f495e8-7bdc-43d7-bfbb-739030d46512_554x438.png 424w, https://substackcdn.com/image/fetch/$s_!GEVp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0f495e8-7bdc-43d7-bfbb-739030d46512_554x438.png 848w, https://substackcdn.com/image/fetch/$s_!GEVp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0f495e8-7bdc-43d7-bfbb-739030d46512_554x438.png 1272w, https://substackcdn.com/image/fetch/$s_!GEVp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0f495e8-7bdc-43d7-bfbb-739030d46512_554x438.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GEVp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0f495e8-7bdc-43d7-bfbb-739030d46512_554x438.png" width="554" height="438" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f0f495e8-7bdc-43d7-bfbb-739030d46512_554x438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:438,&quot;width&quot;:554,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!GEVp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0f495e8-7bdc-43d7-bfbb-739030d46512_554x438.png 424w, https://substackcdn.com/image/fetch/$s_!GEVp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0f495e8-7bdc-43d7-bfbb-739030d46512_554x438.png 848w, https://substackcdn.com/image/fetch/$s_!GEVp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0f495e8-7bdc-43d7-bfbb-739030d46512_554x438.png 1272w, https://substackcdn.com/image/fetch/$s_!GEVp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0f495e8-7bdc-43d7-bfbb-739030d46512_554x438.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.16: Setting the human approval deployment tasks list</em></p><ol start="4"><li><p>Finished! Click the Save button in the top-left corner to save the pipeline.</p></li></ol><p>Now, everything is ready to create our first automatic release. And to do so, a new release can be prepared and deployed as follows:</p><ol><li><p>Click the Create release button to start the creation of a new release, as shown in the following screenshot:</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rUZo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F888da586-3811-45b3-8206-0b76aac18905_670x747.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rUZo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F888da586-3811-45b3-8206-0b76aac18905_670x747.png 424w, https://substackcdn.com/image/fetch/$s_!rUZo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F888da586-3811-45b3-8206-0b76aac18905_670x747.png 848w, https://substackcdn.com/image/fetch/$s_!rUZo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F888da586-3811-45b3-8206-0b76aac18905_670x747.png 1272w, https://substackcdn.com/image/fetch/$s_!rUZo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F888da586-3811-45b3-8206-0b76aac18905_670x747.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rUZo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F888da586-3811-45b3-8206-0b76aac18905_670x747.png" width="670" height="747" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/888da586-3811-45b3-8206-0b76aac18905_670x747.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:747,&quot;width&quot;:670,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!rUZo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F888da586-3811-45b3-8206-0b76aac18905_670x747.png 424w, https://substackcdn.com/image/fetch/$s_!rUZo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F888da586-3811-45b3-8206-0b76aac18905_670x747.png 848w, https://substackcdn.com/image/fetch/$s_!rUZo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F888da586-3811-45b3-8206-0b76aac18905_670x747.png 1272w, https://substackcdn.com/image/fetch/$s_!rUZo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F888da586-3811-45b3-8206-0b76aac18905_670x747.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.17: Creating a new release</em></p><ol start="2"><li><p>Verify that the Source alias is the last available one, add a Release description, and then click Create. In a short time, you should receive an email for release approval. Click the link it contains and go to the approval page:</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!H6Xh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b3d7820-b5be-4b43-b13d-1f506bbaa3f1_808x354.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!H6Xh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b3d7820-b5be-4b43-b13d-1f506bbaa3f1_808x354.png 424w, https://substackcdn.com/image/fetch/$s_!H6Xh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b3d7820-b5be-4b43-b13d-1f506bbaa3f1_808x354.png 848w, https://substackcdn.com/image/fetch/$s_!H6Xh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b3d7820-b5be-4b43-b13d-1f506bbaa3f1_808x354.png 1272w, https://substackcdn.com/image/fetch/$s_!H6Xh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b3d7820-b5be-4b43-b13d-1f506bbaa3f1_808x354.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!H6Xh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b3d7820-b5be-4b43-b13d-1f506bbaa3f1_808x354.png" width="808" height="354" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5b3d7820-b5be-4b43-b13d-1f506bbaa3f1_808x354.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:354,&quot;width&quot;:808,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!H6Xh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b3d7820-b5be-4b43-b13d-1f506bbaa3f1_808x354.png 424w, https://substackcdn.com/image/fetch/$s_!H6Xh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b3d7820-b5be-4b43-b13d-1f506bbaa3f1_808x354.png 848w, https://substackcdn.com/image/fetch/$s_!H6Xh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b3d7820-b5be-4b43-b13d-1f506bbaa3f1_808x354.png 1272w, https://substackcdn.com/image/fetch/$s_!H6Xh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b3d7820-b5be-4b43-b13d-1f506bbaa3f1_808x354.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.18: Approving a release</em></p><ol start="3"><li><p>Click the Approve button to approve the release. Wait for the deployment to complete. You should have all the tasks successfully completed, as shown in the following screenshot:</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-xwp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c438805-7f24-4e44-b822-bafa07f4694d_299x260.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-xwp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c438805-7f24-4e44-b822-bafa07f4694d_299x260.png 424w, https://substackcdn.com/image/fetch/$s_!-xwp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c438805-7f24-4e44-b822-bafa07f4694d_299x260.png 848w, https://substackcdn.com/image/fetch/$s_!-xwp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c438805-7f24-4e44-b822-bafa07f4694d_299x260.png 1272w, https://substackcdn.com/image/fetch/$s_!-xwp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c438805-7f24-4e44-b822-bafa07f4694d_299x260.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-xwp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c438805-7f24-4e44-b822-bafa07f4694d_299x260.png" width="299" height="260" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5c438805-7f24-4e44-b822-bafa07f4694d_299x260.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:260,&quot;width&quot;:299,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!-xwp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c438805-7f24-4e44-b822-bafa07f4694d_299x260.png 424w, https://substackcdn.com/image/fetch/$s_!-xwp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c438805-7f24-4e44-b822-bafa07f4694d_299x260.png 848w, https://substackcdn.com/image/fetch/$s_!-xwp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c438805-7f24-4e44-b822-bafa07f4694d_299x260.png 1272w, https://substackcdn.com/image/fetch/$s_!-xwp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c438805-7f24-4e44-b822-bafa07f4694d_299x260.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.19: Release deployed</em></p><p>You have run your first successful release pipeline!</p><p>In a real-life project, the release pipeline would contain more tasks. In fact, applications (before being deployed in the actual production environment) are deployed in a staging environment where they are beta-tested. Hence, after this first deployment, there would probably be some manual tests, manual authorization for the deployment in production, and the final deployment in production.</p><p>Considering the multistage scenario, you can set up the pipeline in a way where only with defined authorizations will you be able to move from one stage to another:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GGz1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c72e6c-0fc3-4794-bad4-77de9faa0adf_493x254.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GGz1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c72e6c-0fc3-4794-bad4-77de9faa0adf_493x254.png 424w, https://substackcdn.com/image/fetch/$s_!GGz1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c72e6c-0fc3-4794-bad4-77de9faa0adf_493x254.png 848w, https://substackcdn.com/image/fetch/$s_!GGz1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c72e6c-0fc3-4794-bad4-77de9faa0adf_493x254.png 1272w, https://substackcdn.com/image/fetch/$s_!GGz1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c72e6c-0fc3-4794-bad4-77de9faa0adf_493x254.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GGz1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c72e6c-0fc3-4794-bad4-77de9faa0adf_493x254.png" width="493" height="254" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/22c72e6c-0fc3-4794-bad4-77de9faa0adf_493x254.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:254,&quot;width&quot;:493,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!GGz1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c72e6c-0fc3-4794-bad4-77de9faa0adf_493x254.png 424w, https://substackcdn.com/image/fetch/$s_!GGz1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c72e6c-0fc3-4794-bad4-77de9faa0adf_493x254.png 848w, https://substackcdn.com/image/fetch/$s_!GGz1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c72e6c-0fc3-4794-bad4-77de9faa0adf_493x254.png 1272w, https://substackcdn.com/image/fetch/$s_!GGz1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22c72e6c-0fc3-4794-bad4-77de9faa0adf_493x254.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.20: Defining pre-deployment conditions</em></p><p>As you can see in the preceding screenshot, it is quite simple to set up pre-deployment conditions, and there is more than a single option to customize the authorization method. This allows you to refine the CD approach, exactly meeting the needs of the project you are dealing with.</p><p>The following screenshot shows the options provided by Azure DevOps for pre-deployment approval. You can define the people who can approve the stage and set policies for them, that is, revalidate the approver identity before completing the process. You, as a software architect, will need to identify the configuration that fits the project you are creating with this approach:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0_jl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76f758d0-1144-49d7-9cb5-cc2949bbfbc3_873x548.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0_jl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76f758d0-1144-49d7-9cb5-cc2949bbfbc3_873x548.png 424w, https://substackcdn.com/image/fetch/$s_!0_jl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76f758d0-1144-49d7-9cb5-cc2949bbfbc3_873x548.png 848w, https://substackcdn.com/image/fetch/$s_!0_jl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76f758d0-1144-49d7-9cb5-cc2949bbfbc3_873x548.png 1272w, https://substackcdn.com/image/fetch/$s_!0_jl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76f758d0-1144-49d7-9cb5-cc2949bbfbc3_873x548.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0_jl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76f758d0-1144-49d7-9cb5-cc2949bbfbc3_873x548.png" width="873" height="548" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/76f758d0-1144-49d7-9cb5-cc2949bbfbc3_873x548.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:548,&quot;width&quot;:873,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!0_jl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76f758d0-1144-49d7-9cb5-cc2949bbfbc3_873x548.png 424w, https://substackcdn.com/image/fetch/$s_!0_jl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76f758d0-1144-49d7-9cb5-cc2949bbfbc3_873x548.png 848w, https://substackcdn.com/image/fetch/$s_!0_jl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76f758d0-1144-49d7-9cb5-cc2949bbfbc3_873x548.png 1272w, https://substackcdn.com/image/fetch/$s_!0_jl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76f758d0-1144-49d7-9cb5-cc2949bbfbc3_873x548.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.21: Pre-deployment approval options</em></p><p>It is worth mentioning that although this approach is far better than a single-stage deployment, a DevOps pipeline will direct you, as a software architect, to another stage of monitoring. App Insights, which will be presented later, is an incredible tool for this.</p><h2>GitHub</h2><p>Since GitHub&#8217;s acquisition by Microsoft, many features have evolved and new options have been delivered, enhancing the power of this powerful tool. These integrations can be explored using the Azure portal and, particularly, GitHub Actions.</p><p>GitHub Actions is a set of tools that helps with the automation of software development. It enables a fast CI/CD service on any platform, using YAML files to define its workflows. You can consider GitHub Actions a new approach presented by Microsoft as a substitute for Azure DevOps Pipelines. You can automate any GitHub event using GitHub Actions, with thousands of actions available on GitHub Marketplace:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iXci!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda2f67-3d2a-4152-9712-b38ffb7f12a6_871x423.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iXci!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda2f67-3d2a-4152-9712-b38ffb7f12a6_871x423.png 424w, https://substackcdn.com/image/fetch/$s_!iXci!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda2f67-3d2a-4152-9712-b38ffb7f12a6_871x423.png 848w, https://substackcdn.com/image/fetch/$s_!iXci!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda2f67-3d2a-4152-9712-b38ffb7f12a6_871x423.png 1272w, https://substackcdn.com/image/fetch/$s_!iXci!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda2f67-3d2a-4152-9712-b38ffb7f12a6_871x423.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iXci!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda2f67-3d2a-4152-9712-b38ffb7f12a6_871x423.png" width="871" height="423" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9dda2f67-3d2a-4152-9712-b38ffb7f12a6_871x423.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:423,&quot;width&quot;:871,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!iXci!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda2f67-3d2a-4152-9712-b38ffb7f12a6_871x423.png 424w, https://substackcdn.com/image/fetch/$s_!iXci!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda2f67-3d2a-4152-9712-b38ffb7f12a6_871x423.png 848w, https://substackcdn.com/image/fetch/$s_!iXci!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda2f67-3d2a-4152-9712-b38ffb7f12a6_871x423.png 1272w, https://substackcdn.com/image/fetch/$s_!iXci!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda2f67-3d2a-4152-9712-b38ffb7f12a6_871x423.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.22: GitHub Actions</em></p><p>Creating a workflow to build a .NET web app is quite simple via the GitHub Actions interface. As you can see in the preceding screenshot, there are some workflows that have already been created to help us out.</p><p>The YAML we have below was generated by clicking the Set up this workflow option in the Configure options under .NET:</p><pre><code><code>name: .NET
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 8.0.x
    - name: Restore dependencies
      run: dotnet restore
    - name: Build
      run: dotnet build --no-restore
    - name: Test
      run: dotnet test --no-build --verbosity normal
</code></code></pre><p>With the adaptations made below, it can build the application specifically created for this chapter:</p><pre><code><code>name: .NET 8 Chapter 08
on:
  push:
    branches:
    - main
env:
  DOTNET_CORE_VERSION: 8.0.x
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Setup .NET 8
        uses: actions/setup-dotnet@v1
        with:
          include-prerelease: True
          dotnet-version: ${{ env.DOTNET_CORE_VERSION }}
      - name: Install dependencies
        run: dotnet restore ./ch08
      - name: Build
        run: dotnet build ./ch08 --configuration Release --no-restore
      - name: Test
        run: dotnet test ./ch08 --no-restore --verbosity normal
</code></code></pre><p>As you can see below, once the script is updated, it is possible to check the result of the workflow. It is also possible to enable CD if you want to. It is just a matter of defining the correct script:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HqaB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5721c87c-6d66-4a2a-bfd5-bc20f53af287_879x201.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HqaB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5721c87c-6d66-4a2a-bfd5-bc20f53af287_879x201.png 424w, https://substackcdn.com/image/fetch/$s_!HqaB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5721c87c-6d66-4a2a-bfd5-bc20f53af287_879x201.png 848w, https://substackcdn.com/image/fetch/$s_!HqaB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5721c87c-6d66-4a2a-bfd5-bc20f53af287_879x201.png 1272w, https://substackcdn.com/image/fetch/$s_!HqaB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5721c87c-6d66-4a2a-bfd5-bc20f53af287_879x201.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HqaB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5721c87c-6d66-4a2a-bfd5-bc20f53af287_879x201.png" width="879" height="201" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5721c87c-6d66-4a2a-bfd5-bc20f53af287_879x201.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:201,&quot;width&quot;:879,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!HqaB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5721c87c-6d66-4a2a-bfd5-bc20f53af287_879x201.png 424w, https://substackcdn.com/image/fetch/$s_!HqaB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5721c87c-6d66-4a2a-bfd5-bc20f53af287_879x201.png 848w, https://substackcdn.com/image/fetch/$s_!HqaB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5721c87c-6d66-4a2a-bfd5-bc20f53af287_879x201.png 1272w, https://substackcdn.com/image/fetch/$s_!HqaB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5721c87c-6d66-4a2a-bfd5-bc20f53af287_879x201.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 8.23: Simple application compilation using GitHub Actions</em></p><p>Microsoft provides documentation specifically covering Azure and GitHub integration. Check this out at <a href="https://docs.microsoft.com/en-us/azure/developer/github">https://docs.microsoft.com/en-us/azure/developer/github</a>.</p><p>As a software architect, you need to understand which tool best fits your development team. Azure DevOps has a wonderful environment for enabling CI/CD, and so does GitHub. The key point here is that no matter the option you decide on, there are risks and challenges that you will face once CI/CD is enabled. Let&#8217;s check them out in the next topic.</p><h2>Application Insights</h2><p>Application Insights is a feature that any software architect must have for continuous feedback on their solution. Application Insights is part of Azure Monitor, a wider suite of monitoring features that also includes alerting, dashboards, and workbooks. As soon as you connect your app to it, you start receiving feedback on each request made to the software. This enables you to monitor not only the requests made but also your database performance, the errors that the application may be suffering from, and the calls that take the most time to process.</p><p>Obviously, you will have costs relating to having this tool plugged into your environment, but the facilities that the tool provides will be worth it. It might be worth noting that, for simple applications, it could even be free because you pay for data ingested, for which there is a free quota. Aside from financial cost, you need to understand that there is a very small performance cost, since all the requests to store data in Application Insights run in a separate thread.</p><p>Several services, such as App Services, Functions, and so on, will give you the option to add Application Insights as part of the initial creation process, so you may have already created it while following along with this book.</p><p>The following screenshot shows how easily you can create a tool in your environment:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZyAP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40414e1d-42cd-4059-9c15-8ce772b725e0_876x811.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZyAP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40414e1d-42cd-4059-9c15-8ce772b725e0_876x811.png 424w, https://substackcdn.com/image/fetch/$s_!ZyAP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40414e1d-42cd-4059-9c15-8ce772b725e0_876x811.png 848w, https://substackcdn.com/image/fetch/$s_!ZyAP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40414e1d-42cd-4059-9c15-8ce772b725e0_876x811.png 1272w, https://substackcdn.com/image/fetch/$s_!ZyAP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40414e1d-42cd-4059-9c15-8ce772b725e0_876x811.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZyAP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40414e1d-42cd-4059-9c15-8ce772b725e0_876x811.png" width="876" height="811" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/40414e1d-42cd-4059-9c15-8ce772b725e0_876x811.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:811,&quot;width&quot;:876,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Interface gr&#225;fica do usu&#225;rio, Texto, Aplicativo, Email  Descri&#231;&#227;o gerada automaticamente&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Interface gr&#225;fica do usu&#225;rio, Texto, Aplicativo, Email  Descri&#231;&#227;o gerada automaticamente" title="Interface gr&#225;fica do usu&#225;rio, Texto, Aplicativo, Email  Descri&#231;&#227;o gerada automaticamente" srcset="https://substackcdn.com/image/fetch/$s_!ZyAP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40414e1d-42cd-4059-9c15-8ce772b725e0_876x811.png 424w, https://substackcdn.com/image/fetch/$s_!ZyAP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40414e1d-42cd-4059-9c15-8ce772b725e0_876x811.png 848w, https://substackcdn.com/image/fetch/$s_!ZyAP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40414e1d-42cd-4059-9c15-8ce772b725e0_876x811.png 1272w, https://substackcdn.com/image/fetch/$s_!ZyAP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40414e1d-42cd-4059-9c15-8ce772b725e0_876x811.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.24: Creating Application Insights resources in Azure</em></p><p>For instance, let&#8217;s suppose you need to analyze the requests that are taking more time in your application. The process of attaching Application Insights to your web app is quite simple: it can be done as soon as you set it up. If you are not sure whether Application Insights is configured for your web app, you can find out using the Azure portal. Navigate to App Services and look at the Application Insights settings, as shown in the following screenshot:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Zboc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64d254f6-f4b5-4bcf-bbce-02cd507206c7_876x499.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Zboc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64d254f6-f4b5-4bcf-bbce-02cd507206c7_876x499.png 424w, https://substackcdn.com/image/fetch/$s_!Zboc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64d254f6-f4b5-4bcf-bbce-02cd507206c7_876x499.png 848w, https://substackcdn.com/image/fetch/$s_!Zboc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64d254f6-f4b5-4bcf-bbce-02cd507206c7_876x499.png 1272w, https://substackcdn.com/image/fetch/$s_!Zboc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64d254f6-f4b5-4bcf-bbce-02cd507206c7_876x499.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Zboc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64d254f6-f4b5-4bcf-bbce-02cd507206c7_876x499.png" width="876" height="499" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/64d254f6-f4b5-4bcf-bbce-02cd507206c7_876x499.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:499,&quot;width&quot;:876,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Zboc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64d254f6-f4b5-4bcf-bbce-02cd507206c7_876x499.png 424w, https://substackcdn.com/image/fetch/$s_!Zboc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64d254f6-f4b5-4bcf-bbce-02cd507206c7_876x499.png 848w, https://substackcdn.com/image/fetch/$s_!Zboc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64d254f6-f4b5-4bcf-bbce-02cd507206c7_876x499.png 1272w, https://substackcdn.com/image/fetch/$s_!Zboc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64d254f6-f4b5-4bcf-bbce-02cd507206c7_876x499.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.25: Enabling Application Insights in App Services</em></p><p>The interface will give you the opportunity to create or attach an already-created monitoring service to your web app. You can connect more than one web app to the same Application Insights component.</p><p>The following screenshot shows how to add a web app to an already-created Application Insights resource:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fJLd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2390a8fc-50fb-48b8-819f-8823d1ab5225_879x602.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fJLd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2390a8fc-50fb-48b8-819f-8823d1ab5225_879x602.png 424w, https://substackcdn.com/image/fetch/$s_!fJLd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2390a8fc-50fb-48b8-819f-8823d1ab5225_879x602.png 848w, https://substackcdn.com/image/fetch/$s_!fJLd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2390a8fc-50fb-48b8-819f-8823d1ab5225_879x602.png 1272w, https://substackcdn.com/image/fetch/$s_!fJLd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2390a8fc-50fb-48b8-819f-8823d1ab5225_879x602.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fJLd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2390a8fc-50fb-48b8-819f-8823d1ab5225_879x602.png" width="879" height="602" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2390a8fc-50fb-48b8-819f-8823d1ab5225_879x602.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:602,&quot;width&quot;:879,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!fJLd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2390a8fc-50fb-48b8-819f-8823d1ab5225_879x602.png 424w, https://substackcdn.com/image/fetch/$s_!fJLd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2390a8fc-50fb-48b8-819f-8823d1ab5225_879x602.png 848w, https://substackcdn.com/image/fetch/$s_!fJLd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2390a8fc-50fb-48b8-819f-8823d1ab5225_879x602.png 1272w, https://substackcdn.com/image/fetch/$s_!fJLd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2390a8fc-50fb-48b8-819f-8823d1ab5225_879x602.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.26: Enabling App Insights in App Services</em></p><p>Once you have Application Insights configured for your web app, you will see the following screen in App Services:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XrdV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4fa60dd-d024-41a2-8832-06f299c539cb_879x657.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XrdV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4fa60dd-d024-41a2-8832-06f299c539cb_879x657.png 424w, https://substackcdn.com/image/fetch/$s_!XrdV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4fa60dd-d024-41a2-8832-06f299c539cb_879x657.png 848w, https://substackcdn.com/image/fetch/$s_!XrdV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4fa60dd-d024-41a2-8832-06f299c539cb_879x657.png 1272w, https://substackcdn.com/image/fetch/$s_!XrdV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4fa60dd-d024-41a2-8832-06f299c539cb_879x657.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XrdV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4fa60dd-d024-41a2-8832-06f299c539cb_879x657.png" width="879" height="657" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d4fa60dd-d024-41a2-8832-06f299c539cb_879x657.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:657,&quot;width&quot;:879,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Graphical user interface, text, application, email  Description automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Graphical user interface, text, application, email  Description automatically generated" title="Graphical user interface, text, application, email  Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!XrdV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4fa60dd-d024-41a2-8832-06f299c539cb_879x657.png 424w, https://substackcdn.com/image/fetch/$s_!XrdV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4fa60dd-d024-41a2-8832-06f299c539cb_879x657.png 848w, https://substackcdn.com/image/fetch/$s_!XrdV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4fa60dd-d024-41a2-8832-06f299c539cb_879x657.png 1272w, https://substackcdn.com/image/fetch/$s_!XrdV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4fa60dd-d024-41a2-8832-06f299c539cb_879x657.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.27: Application Insights in App Services</em></p><p>Once it is connected to your solution, the data collection will happen continuously, and you will see the results in the dashboard provided by the component. You can find this screen in two places:</p><ul><li><p>In the same place where you configured Application Insights, inside the web app portal</p></li><li><p>In the Azure portal, after navigating through the Application Insights resource</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Uxhn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb727887-0da2-40c8-8ddd-96266a767d91_813x427.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Uxhn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb727887-0da2-40c8-8ddd-96266a767d91_813x427.png 424w, https://substackcdn.com/image/fetch/$s_!Uxhn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb727887-0da2-40c8-8ddd-96266a767d91_813x427.png 848w, https://substackcdn.com/image/fetch/$s_!Uxhn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb727887-0da2-40c8-8ddd-96266a767d91_813x427.png 1272w, https://substackcdn.com/image/fetch/$s_!Uxhn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb727887-0da2-40c8-8ddd-96266a767d91_813x427.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Uxhn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb727887-0da2-40c8-8ddd-96266a767d91_813x427.png" width="813" height="427" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/db727887-0da2-40c8-8ddd-96266a767d91_813x427.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:427,&quot;width&quot;:813,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Uxhn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb727887-0da2-40c8-8ddd-96266a767d91_813x427.png 424w, https://substackcdn.com/image/fetch/$s_!Uxhn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb727887-0da2-40c8-8ddd-96266a767d91_813x427.png 848w, https://substackcdn.com/image/fetch/$s_!Uxhn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb727887-0da2-40c8-8ddd-96266a767d91_813x427.png 1272w, https://substackcdn.com/image/fetch/$s_!Uxhn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb727887-0da2-40c8-8ddd-96266a767d91_813x427.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.28: Application Insights in action</em></p><p>This dashboard gives you an idea of failed requests, server response time, and server requests. You can also turn on the availability check, which will make requests to your selected URL from any of the Azure data centers.</p><p>The beauty of Application Insights is in how deeply it analyzes your system. In the following screenshot, for instance, it gives you feedback on the number of requests made on the website. You can analyze this feedback by ranking the requests by the ones that took more time to process or the ones that were called more often.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!s98w!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8603ea1-3eca-416f-a597-2f9ced27edd3_876x681.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!s98w!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8603ea1-3eca-416f-a597-2f9ced27edd3_876x681.png 424w, https://substackcdn.com/image/fetch/$s_!s98w!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8603ea1-3eca-416f-a597-2f9ced27edd3_876x681.png 848w, https://substackcdn.com/image/fetch/$s_!s98w!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8603ea1-3eca-416f-a597-2f9ced27edd3_876x681.png 1272w, https://substackcdn.com/image/fetch/$s_!s98w!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8603ea1-3eca-416f-a597-2f9ced27edd3_876x681.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!s98w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8603ea1-3eca-416f-a597-2f9ced27edd3_876x681.png" width="876" height="681" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d8603ea1-3eca-416f-a597-2f9ced27edd3_876x681.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:681,&quot;width&quot;:876,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Graphical user interface, text, application, email  Description automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Graphical user interface, text, application, email  Description automatically generated" title="Graphical user interface, text, application, email  Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!s98w!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8603ea1-3eca-416f-a597-2f9ced27edd3_876x681.png 424w, https://substackcdn.com/image/fetch/$s_!s98w!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8603ea1-3eca-416f-a597-2f9ced27edd3_876x681.png 848w, https://substackcdn.com/image/fetch/$s_!s98w!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8603ea1-3eca-416f-a597-2f9ced27edd3_876x681.png 1272w, https://substackcdn.com/image/fetch/$s_!s98w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8603ea1-3eca-416f-a597-2f9ced27edd3_876x681.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.29: Analyzing app performance using Application Insights</em></p><p>Considering this view can be filtered in different ways and you receive the information just after it happens in your web app, this is certainly a tool that defines continuous feedback. This is one of the best ways you can use DevOps principles to achieve exactly what your customer needs.</p><h2>Test and Feedback</h2><p>Another useful tool in the process of continuous feedback is the Test and Feedback tool, designed by Microsoft to help product owners and quality assurance users with the process of analyzing new features.</p><p>Using Azure DevOps, you can ask for feedback from your team by selecting an option inside each work item, as you can see in the following screenshot:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3Lwd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc70507-2781-4c37-b64f-6a1d70a7a460_875x508.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3Lwd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc70507-2781-4c37-b64f-6a1d70a7a460_875x508.png 424w, https://substackcdn.com/image/fetch/$s_!3Lwd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc70507-2781-4c37-b64f-6a1d70a7a460_875x508.png 848w, https://substackcdn.com/image/fetch/$s_!3Lwd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc70507-2781-4c37-b64f-6a1d70a7a460_875x508.png 1272w, https://substackcdn.com/image/fetch/$s_!3Lwd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc70507-2781-4c37-b64f-6a1d70a7a460_875x508.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3Lwd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc70507-2781-4c37-b64f-6a1d70a7a460_875x508.png" width="875" height="508" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdc70507-2781-4c37-b64f-6a1d70a7a460_875x508.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:508,&quot;width&quot;:875,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Graphical user interface, application  Description automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Graphical user interface, application  Description automatically generated" title="Graphical user interface, application  Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!3Lwd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc70507-2781-4c37-b64f-6a1d70a7a460_875x508.png 424w, https://substackcdn.com/image/fetch/$s_!3Lwd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc70507-2781-4c37-b64f-6a1d70a7a460_875x508.png 848w, https://substackcdn.com/image/fetch/$s_!3Lwd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc70507-2781-4c37-b64f-6a1d70a7a460_875x508.png 1272w, https://substackcdn.com/image/fetch/$s_!3Lwd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc70507-2781-4c37-b64f-6a1d70a7a460_875x508.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.30: Requesting feedback using Azure DevOps</em></p><p>Once someone receives a feedback request, they can use the Test and Feedback tool to analyze and give the correct feedback to the team. They will be able to connect the tool to your Azure DevOps project, giving you more features while analyzing the feedback request.</p><p>You can download this tool from <a href="https://marketplace.visualstudio.com/items?itemName=ms.vss-exploratorytesting-web">https://marketplace.visualstudio.com/items?itemName=ms.vss-exploratorytesting-web</a>.</p><p>This tool is a web browser extension that you will need to install before use. The following screenshot shows how to set up an Azure DevOps project URL for the Test and Feedback tool:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aHRR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3953a6c-999b-4a36-a6ba-3476823801e6_523x438.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aHRR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3953a6c-999b-4a36-a6ba-3476823801e6_523x438.png 424w, https://substackcdn.com/image/fetch/$s_!aHRR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3953a6c-999b-4a36-a6ba-3476823801e6_523x438.png 848w, https://substackcdn.com/image/fetch/$s_!aHRR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3953a6c-999b-4a36-a6ba-3476823801e6_523x438.png 1272w, https://substackcdn.com/image/fetch/$s_!aHRR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3953a6c-999b-4a36-a6ba-3476823801e6_523x438.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aHRR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3953a6c-999b-4a36-a6ba-3476823801e6_523x438.png" width="523" height="438" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f3953a6c-999b-4a36-a6ba-3476823801e6_523x438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:438,&quot;width&quot;:523,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!aHRR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3953a6c-999b-4a36-a6ba-3476823801e6_523x438.png 424w, https://substackcdn.com/image/fetch/$s_!aHRR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3953a6c-999b-4a36-a6ba-3476823801e6_523x438.png 848w, https://substackcdn.com/image/fetch/$s_!aHRR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3953a6c-999b-4a36-a6ba-3476823801e6_523x438.png 1272w, https://substackcdn.com/image/fetch/$s_!aHRR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3953a6c-999b-4a36-a6ba-3476823801e6_523x438.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.31: Connecting Test and Feedback to an Azure DevOps organization</em></p><p>The tool is quite simple. You can take screenshots, record a process, or even make a note. The following screenshot shows how easily you can write a message inside a screenshot:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zQ1s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd29e96-39f1-48f8-8ce8-5243c797622c_848x518.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zQ1s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd29e96-39f1-48f8-8ce8-5243c797622c_848x518.png 424w, https://substackcdn.com/image/fetch/$s_!zQ1s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd29e96-39f1-48f8-8ce8-5243c797622c_848x518.png 848w, https://substackcdn.com/image/fetch/$s_!zQ1s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd29e96-39f1-48f8-8ce8-5243c797622c_848x518.png 1272w, https://substackcdn.com/image/fetch/$s_!zQ1s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd29e96-39f1-48f8-8ce8-5243c797622c_848x518.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zQ1s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd29e96-39f1-48f8-8ce8-5243c797622c_848x518.png" width="848" height="518" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7bd29e96-39f1-48f8-8ce8-5243c797622c_848x518.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:518,&quot;width&quot;:848,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Graphical user interface  Description automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Graphical user interface  Description automatically generated" title="Graphical user interface  Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!zQ1s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd29e96-39f1-48f8-8ce8-5243c797622c_848x518.png 424w, https://substackcdn.com/image/fetch/$s_!zQ1s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd29e96-39f1-48f8-8ce8-5243c797622c_848x518.png 848w, https://substackcdn.com/image/fetch/$s_!zQ1s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd29e96-39f1-48f8-8ce8-5243c797622c_848x518.png 1272w, https://substackcdn.com/image/fetch/$s_!zQ1s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd29e96-39f1-48f8-8ce8-5243c797622c_848x518.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.32: Giving feedback with the Test and Feedback tool</em></p><p>The good thing is that you record all this analysis in a session timeline. As you can see in the next screenshot, you can have multiple pieces of feedback in the same session, which is good for the analysis process:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PA_8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0da1599-0372-47de-a8ac-2f12124c6743_570x640.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PA_8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0da1599-0372-47de-a8ac-2f12124c6743_570x640.png 424w, https://substackcdn.com/image/fetch/$s_!PA_8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0da1599-0372-47de-a8ac-2f12124c6743_570x640.png 848w, https://substackcdn.com/image/fetch/$s_!PA_8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0da1599-0372-47de-a8ac-2f12124c6743_570x640.png 1272w, https://substackcdn.com/image/fetch/$s_!PA_8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0da1599-0372-47de-a8ac-2f12124c6743_570x640.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PA_8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0da1599-0372-47de-a8ac-2f12124c6743_570x640.png" width="570" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b0da1599-0372-47de-a8ac-2f12124c6743_570x640.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:570,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!PA_8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0da1599-0372-47de-a8ac-2f12124c6743_570x640.png 424w, https://substackcdn.com/image/fetch/$s_!PA_8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0da1599-0372-47de-a8ac-2f12124c6743_570x640.png 848w, https://substackcdn.com/image/fetch/$s_!PA_8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0da1599-0372-47de-a8ac-2f12124c6743_570x640.png 1272w, https://substackcdn.com/image/fetch/$s_!PA_8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0da1599-0372-47de-a8ac-2f12124c6743_570x640.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.33: Giving feedback with the Test and Feedback tool</em></p><p>Once you have done the analysis and you are connected to Azure DevOps, you will be able to report a bug (as shown in the following screenshot), create a task, or even start a new test case:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bIMa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f8f97c8-38ba-4bb3-89df-71da84634d7d_882x390.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bIMa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f8f97c8-38ba-4bb3-89df-71da84634d7d_882x390.png 424w, https://substackcdn.com/image/fetch/$s_!bIMa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f8f97c8-38ba-4bb3-89df-71da84634d7d_882x390.png 848w, https://substackcdn.com/image/fetch/$s_!bIMa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f8f97c8-38ba-4bb3-89df-71da84634d7d_882x390.png 1272w, https://substackcdn.com/image/fetch/$s_!bIMa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f8f97c8-38ba-4bb3-89df-71da84634d7d_882x390.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bIMa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f8f97c8-38ba-4bb3-89df-71da84634d7d_882x390.png" width="882" height="390" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9f8f97c8-38ba-4bb3-89df-71da84634d7d_882x390.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:390,&quot;width&quot;:882,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!bIMa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f8f97c8-38ba-4bb3-89df-71da84634d7d_882x390.png 424w, https://substackcdn.com/image/fetch/$s_!bIMa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f8f97c8-38ba-4bb3-89df-71da84634d7d_882x390.png 848w, https://substackcdn.com/image/fetch/$s_!bIMa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f8f97c8-38ba-4bb3-89df-71da84634d7d_882x390.png 1272w, https://substackcdn.com/image/fetch/$s_!bIMa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f8f97c8-38ba-4bb3-89df-71da84634d7d_882x390.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.34: Opening a bug in Azure DevOps</em></p><p>The result of the bug created can be checked on the Work items board in Azure DevOps. It is worth mentioning that you do not need an Azure DevOps developer license to have access to this area of the environment. This enables you, as a software architect, to share this basic and useful tool with as many key users of the solution as you have.</p><p>The following screenshot shows the bug created by the tool once you have connected it to your Azure DevOps project:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5_n_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcd7e9bf-1d66-41ea-922e-398071cb0d57_882x733.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5_n_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcd7e9bf-1d66-41ea-922e-398071cb0d57_882x733.png 424w, https://substackcdn.com/image/fetch/$s_!5_n_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcd7e9bf-1d66-41ea-922e-398071cb0d57_882x733.png 848w, https://substackcdn.com/image/fetch/$s_!5_n_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcd7e9bf-1d66-41ea-922e-398071cb0d57_882x733.png 1272w, https://substackcdn.com/image/fetch/$s_!5_n_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcd7e9bf-1d66-41ea-922e-398071cb0d57_882x733.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5_n_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcd7e9bf-1d66-41ea-922e-398071cb0d57_882x733.png" width="882" height="733" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fcd7e9bf-1d66-41ea-922e-398071cb0d57_882x733.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:733,&quot;width&quot;:882,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!5_n_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcd7e9bf-1d66-41ea-922e-398071cb0d57_882x733.png 424w, https://substackcdn.com/image/fetch/$s_!5_n_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcd7e9bf-1d66-41ea-922e-398071cb0d57_882x733.png 848w, https://substackcdn.com/image/fetch/$s_!5_n_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcd7e9bf-1d66-41ea-922e-398071cb0d57_882x733.png 1272w, https://substackcdn.com/image/fetch/$s_!5_n_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcd7e9bf-1d66-41ea-922e-398071cb0d57_882x733.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 8.35: New reported bug in Azure DevOps</em></p><p>It is important to have a tool like this to get good feedback on your project. But, as a software architect, you may have to find the best solutions to hasten the feedback process.</p><h1>Summary</h1><p>In this chapter, we have learned that DevOps is not only a bunch of techniques and tools used together to deliver software continuously but also a philosophy to enable continuous delivery of value to the end user of the project you are developing.</p><p>Considering this approach, we saw how CI/CD and continuous feedback are essential to the purpose of DevOps. We also saw how Azure, Azure DevOps, GitHub, and Microsoft tools help you to achieve your goals.</p><p>The chapter also covered the importance of understanding when you can enable CI/CD in the software development life cycle, considering the risks and challenges you will take as a software architect once you decide to use it for your solution.</p><p>Additionally, the chapter introduced some solutions and concepts that can make this process easier, such as multi-stage environments, PR reviews, feature flags, peer reviews, and automated tests. Understanding these techniques and processes will enable you to guide your project toward safer behavior when it comes to CI/CD in a DevOps scenario.</p><p>We also described <em>service design thinking</em> principles. Now, you should be able to analyze all the implications of these approaches for an organization, and you should be able to adapt pre-existing software development processes and hardware/software architectures to take advantage of the opportunities they offer.</p><p>We also explained the need for, and the techniques involved in, the automation of the software cycle, cloud hardware infrastructure configuration, and application deployment.</p><p>Once you have implemented the examples shown, you should be able to use Azure Pipelines to automate infrastructure configuration and application deployment. This chapter elucidated this approach using WWTravelClub as an example, enabling CI/CD inside Azure DevOps, and using Application Insights and the Test and Feedback tool for both technical and functional feedback. In real life, these tools will enable you to understand the current behavior of the system you are developing more quickly, as you will have continuous feedback on it.</p><p>In the next chapter, we will see how automation for software testing works.</p><h1>Questions</h1><ol><li><p>What is DevOps?</p></li><li><p>What is continuous integration (CI)?</p></li><li><p>What is continuous delivery (CD)?</p></li><li><p>Can you have DevOps without CI/CD?</p></li><li><p>What are the risks of enabling CI/CD in a non-mature team?</p></li><li><p>How can a multi-stage environment help CI/CD?</p></li><li><p>How can automated tests help CI/CD?</p></li><li><p>How can PRs help CI/CD?</p></li><li><p>Do PRs only work with CI/CD?</p></li><li><p>What is continuous feedback?</p></li><li><p>What is the difference between the build and release pipelines?</p></li><li><p>What is the main purpose of Application Insights in the DevOps approach?</p></li><li><p>How can the Test and Feedback tool help in the process of DevOps?</p></li><li><p>What is the main goal of service design thinking?</p></li><li><p>What is the preferred Azure tool for the automation of the whole application life cycle?</p></li></ol><h2>Learn more on Discord</h2><p>To join the Discord community for this book &#8211; where you can share feedback, ask questions to the authors, and learn about new releases &#8211; follow the QR code below:</p><p><a href="https://packt.link/SoftwareArchitectureCSharp12Dotnet8">https://packt.link/SoftwareArchitectureCSharp12Dotnet8</a></p><div><hr></div><p>To learn how to build scalable enterprise systems with microservices, apply architectural patterns like Domain-Driven Design, and prepare .NET applications for Kubernetes and cloud-native deployments&#8212;check out <em><strong><a href="https://www.packtpub.com/en-us/product/software-architecture-with-c-12-and-net-8-9781805122456">Software Architecture with C# 12 and .NET 8</a></strong></em> by Gabriel Baptista and Francesco Abbruzzese, available from Packt. Now in its fourth edition, the book combines design fundamentals with hands-on .NET practices, covering everything from EF Core and DevOps pipelines to Blazor, OpenTelemetry, and a complete case study centered on a real-world travel agency system.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.packtpub.com/en-us/product/software-architecture-with-c-12-and-net-8-9781805127659" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!97w9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e19a2d8-04b9-4ddb-ac35-984fdf6d6eca_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!97w9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e19a2d8-04b9-4ddb-ac35-984fdf6d6eca_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!97w9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e19a2d8-04b9-4ddb-ac35-984fdf6d6eca_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!97w9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e19a2d8-04b9-4ddb-ac35-984fdf6d6eca_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!97w9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e19a2d8-04b9-4ddb-ac35-984fdf6d6eca_2250x2775" width="262" height="323.18131868131866" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e19a2d8-04b9-4ddb-ac35-984fdf6d6eca_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:262,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Software Architecture with C# 12 and .NET 8&quot;,&quot;title&quot;:&quot;Software Architecture with C# 12 and .NET 8&quot;,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.packtpub.com/en-us/product/software-architecture-with-c-12-and-net-8-9781805127659&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Software Architecture with C# 12 and .NET 8" title="Software Architecture with C# 12 and .NET 8" srcset="https://substackcdn.com/image/fetch/$s_!97w9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e19a2d8-04b9-4ddb-ac35-984fdf6d6eca_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!97w9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e19a2d8-04b9-4ddb-ac35-984fdf6d6eca_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!97w9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e19a2d8-04b9-4ddb-ac35-984fdf6d6eca_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!97w9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e19a2d8-04b9-4ddb-ac35-984fdf6d6eca_2250x2775 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here is what some readers have said:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DAp7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa809a105-0e2a-4885-b0f7-e3d74e6894e9_1082x632.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DAp7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa809a105-0e2a-4885-b0f7-e3d74e6894e9_1082x632.png 424w, https://substackcdn.com/image/fetch/$s_!DAp7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa809a105-0e2a-4885-b0f7-e3d74e6894e9_1082x632.png 848w, https://substackcdn.com/image/fetch/$s_!DAp7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa809a105-0e2a-4885-b0f7-e3d74e6894e9_1082x632.png 1272w, https://substackcdn.com/image/fetch/$s_!DAp7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa809a105-0e2a-4885-b0f7-e3d74e6894e9_1082x632.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DAp7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa809a105-0e2a-4885-b0f7-e3d74e6894e9_1082x632.png" width="1082" height="632" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a809a105-0e2a-4885-b0f7-e3d74e6894e9_1082x632.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:632,&quot;width&quot;:1082,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:167827,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/167883846?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa809a105-0e2a-4885-b0f7-e3d74e6894e9_1082x632.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!DAp7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa809a105-0e2a-4885-b0f7-e3d74e6894e9_1082x632.png 424w, https://substackcdn.com/image/fetch/$s_!DAp7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa809a105-0e2a-4885-b0f7-e3d74e6894e9_1082x632.png 848w, https://substackcdn.com/image/fetch/$s_!DAp7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa809a105-0e2a-4885-b0f7-e3d74e6894e9_1082x632.png 1272w, https://substackcdn.com/image/fetch/$s_!DAp7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa809a105-0e2a-4885-b0f7-e3d74e6894e9_1082x632.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xLCR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aeecf4c-c3f2-4de3-9167-8d84768a0fbf_1063x315.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xLCR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aeecf4c-c3f2-4de3-9167-8d84768a0fbf_1063x315.png 424w, https://substackcdn.com/image/fetch/$s_!xLCR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aeecf4c-c3f2-4de3-9167-8d84768a0fbf_1063x315.png 848w, https://substackcdn.com/image/fetch/$s_!xLCR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aeecf4c-c3f2-4de3-9167-8d84768a0fbf_1063x315.png 1272w, https://substackcdn.com/image/fetch/$s_!xLCR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aeecf4c-c3f2-4de3-9167-8d84768a0fbf_1063x315.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xLCR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aeecf4c-c3f2-4de3-9167-8d84768a0fbf_1063x315.png" width="1063" height="315" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3aeecf4c-c3f2-4de3-9167-8d84768a0fbf_1063x315.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:315,&quot;width&quot;:1063,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:64077,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/167883846?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aeecf4c-c3f2-4de3-9167-8d84768a0fbf_1063x315.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!xLCR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aeecf4c-c3f2-4de3-9167-8d84768a0fbf_1063x315.png 424w, https://substackcdn.com/image/fetch/$s_!xLCR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aeecf4c-c3f2-4de3-9167-8d84768a0fbf_1063x315.png 848w, https://substackcdn.com/image/fetch/$s_!xLCR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aeecf4c-c3f2-4de3-9167-8d84768a0fbf_1063x315.png 1272w, https://substackcdn.com/image/fetch/$s_!xLCR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3aeecf4c-c3f2-4de3-9167-8d84768a0fbf_1063x315.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!F-h-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7e41715-4a27-495e-83aa-d4a2c049c0ca_1087x426.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!F-h-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7e41715-4a27-495e-83aa-d4a2c049c0ca_1087x426.png 424w, https://substackcdn.com/image/fetch/$s_!F-h-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7e41715-4a27-495e-83aa-d4a2c049c0ca_1087x426.png 848w, https://substackcdn.com/image/fetch/$s_!F-h-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7e41715-4a27-495e-83aa-d4a2c049c0ca_1087x426.png 1272w, https://substackcdn.com/image/fetch/$s_!F-h-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7e41715-4a27-495e-83aa-d4a2c049c0ca_1087x426.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!F-h-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7e41715-4a27-495e-83aa-d4a2c049c0ca_1087x426.png" width="1087" height="426" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d7e41715-4a27-495e-83aa-d4a2c049c0ca_1087x426.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:426,&quot;width&quot;:1087,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:110272,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/167883846?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7e41715-4a27-495e-83aa-d4a2c049c0ca_1087x426.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!F-h-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7e41715-4a27-495e-83aa-d4a2c049c0ca_1087x426.png 424w, https://substackcdn.com/image/fetch/$s_!F-h-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7e41715-4a27-495e-83aa-d4a2c049c0ca_1087x426.png 848w, https://substackcdn.com/image/fetch/$s_!F-h-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7e41715-4a27-495e-83aa-d4a2c049c0ca_1087x426.png 1272w, https://substackcdn.com/image/fetch/$s_!F-h-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7e41715-4a27-495e-83aa-d4a2c049c0ca_1087x426.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Large-Scale Algorithms]]></title><description><![CDATA[The complete &#8220;Chapter 15: Large&#8209;Scale Algorithms&#8221; from the book 50 Algorithms Every Programmer Should Know by Imran Ahmad (Packt, September 2023).]]></description><link>https://deepengineering.substack.com/p/large-scale-algorithms</link><guid isPermaLink="false">https://deepengineering.substack.com/p/large-scale-algorithms</guid><dc:creator><![CDATA[Imran Ahmad]]></dc:creator><pubDate>Thu, 26 Jun 2025 08:10:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!PRXG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eac8c69-1aa2-4889-ae46-01efbf39b27f_825x384.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Large-scale algorithms are specifically designed to tackle sizable and intricate problems. They distinguish themselves by their demand for multiple execution engines due to the sheer volume of data and processing requirements. Examples of such algorithms include Large Language Models (LLMs) like ChatGPT, which require distributed model training to manage the extensive computational demands inherent to deep learning. The resource-intensive nature of such complex algorithms highlights the requirement for robust, parallel processing techniques critical for training the model.</p><p>In this chapter, we will start by introducing the concept of large-scale algorithms and then proceed to discuss the efficient infrastructure required to support them. Additionally, we will explore various strategies for managing multi-resource processing. Within this chapter, we will examine the limitations of parallel processing, as outlined by Amdahl&#8217;s law, and investigate the use of Graphics Processing Units (GPUs). Upon completing this chapter, you will have gained a solid foundation in the fundamental strategies essential for designing large-scale algorithms.</p><p>The topics covered in this chapter include:</p><ul><li><p>Introduction to large-scale algorithms</p></li><li><p>Efficient infrastructure for large-scale algorithms</p></li><li><p>Strategizing multi-resource processing</p></li><li><p>Using the power of clusters/cloud to run large-scale algorithms</p></li></ul><p>Let&#8217;s start with the introduction.</p><h1>Introduction to large-scale algorithms</h1><p>Throughout history, humans have tackled complex problems, from predicting locust swarm locations to discovering the largest prime numbers. Our curiosity and determination have led to continuous innovation in problem-solving methods. The invention of computers was a pivotal moment in this journey, giving us the ability to handle intricate algorithms and calculations. Nowadays, computers enable us to process massive datasets, execute complex computations, and simulate various scenarios with remarkable speed and accuracy.</p><p>However, as we encounter increasingly complex challenges, the resources of a single computer often prove insufficient. This is where large-scale algorithms come into play, harnessing the combined power of multiple computers working together. Large-scale algorithm design constitutes a dynamic and extensive field within computer science, focusing on creating and analyzing algorithms that efficiently utilize the computational resources of numerous machines. These large-scale algorithms allow two types of computing &#8211; distributed and parallel. In distributed computing, we divide a single task between multiple computers. They each work on a segment of the task and combine their results at the end. Think of it like assembling a car: different workers handle different parts, but together, they build the entire vehicle. Parallel computing, conversely, involves multiple processors performing multiple tasks simultaneously, similar to an assembly line where every worker does a different job at the same time.</p><p>LLMs, such as OpenAI&#8217;s GPT-4, hold a crucial position in this vast domain, as they represent a form of large-scale algorithms. LLMs are designed to comprehend and generate human-like text by processing extensive amounts of data and identifying patterns within languages. However, training these models is a heavy-duty task. It involves working with billions, sometimes trillions, of data units, known as tokens. This training includes steps that need to be done one by one, like getting the data ready. There are also steps that can be done at the same time, like figuring out the changes needed across different layers of the model.</p><p>It&#8217;s not an understatement to say that this is a massive job. Because of this scale, it&#8217;s a common practice to train LLMs using multiple computers at once. We call these &#8220;distributed systems.&#8221; These systems use several GPUs &#8211; these are the parts of computers that do the heavy lifting for creating images or processing data. It&#8217;s more accurate to say that LLMs are almost always trained on many machines working together to teach a single model.</p><p>In this context, let us first characterize a well-designed large-scale algorithm, one that can fully harness the potential of modern computing infrastructure, such as cloud computing, clusters, and GPUs/TPUs.</p><h1>Characterizing performant infrastructure for large-scale algorithms</h1><p>To efficiently run large-scale algorithms, we want performant systems as they are designed to handle increased workloads by adding more computing resources to distribute the processing. Horizontal scaling is a key technique for achieving scalability in distributed systems, enabling the system to expand its capacity by allocating tasks to multiple resources. These resources are typically hardware (like Central Processing Units (CPUs) or GPUs) or software elements (like memory, disk space, or network bandwidth) that the system can utilize to perform tasks. For a scalable system to efficiently address computational requirements, it should exhibit elasticity and load balancing, as discussed in the following section.</p><h2>Elasticity</h2><p>Elasticity refers to the capacity of infrastructure to dynamically scale resources according to changing requirements. One common method of implementing this feature is autoscaling, a prevalent strategy in cloud computing platforms such as Amazon Web Services (AWS). In the context of cloud computing, a server group is a collection of virtual servers or instances that are orchestrated to work together to handle specific workloads. These server groups can be organized into clusters to provide high availability, fault tolerance, and load balancing. Each server within a group can be configured with specific resources, such as CPU, memory, and storage, to perform optimally for the intended tasks. Autoscaling allows the server group to adapt to fluctuating demands by modifying the number of nodes (virtual servers) in operation. In an elastic system, resources can be added (scaling out) to accommodate increased demand, and similarly, resources can be released (scaling in) when the demand decreases. This dynamic adjustment allows for efficient use of resources, helping to balance performance needs with cost-effectiveness.</p><p>AWS provides an autoscaling service, which integrates with other AWS services like EC2 (Elastic Compute Cloud) and ELB (Elastic Load Balancing), to automatically adjust the number of server instances in the group. This ensures optimal resource allocation and consistent performance, even during periods of heavy traffic or system failures.</p><h2>Characterizing a well-designed, large-scale algorithm</h2><p>A well-designed, large-scale algorithm is capable of processing vast amounts of information and is designed to be adaptable, resilient, and efficient. It is resilient and adaptable to accommodate the fluctuating dynamics of a large-scale environment.</p><p>A well-designed, large-scale algorithm has the following two characteristics:</p><ul><li><p>Parallelism: Parallelism is a feature that lets an algorithm do several things at once. For big computing jobs, an algorithm should be able to divide tasks between many computers. This speeds up calculations because they are happening all at the same time. In the context of large-scale computing, an algorithm should be capable of splitting tasks across multiple machines, thereby expediting computations through simultaneous processing.</p></li><li><p>Fault tolerance: Given the increased risk of system failures in large-scale environments due to the sheer number of components, it&#8217;s essential that algorithms are built to withstand these faults. They should be able to recover from failures without substantial loss of data or inaccuracies in output.</p></li></ul><p>The three cloud computing giants, Google, Amazon, and Microsoft, provide highly elastic infrastructures. Due to the gigantic size of their shared resource pools, there are very few companies that have the potential to match the elasticity of the infrastructure of these three companies.</p><p>The performance of a large-scale algorithm is intricately linked to the quality of the underlying infrastructure. This foundation should provide adequate computational resources, extensive storage capacity, high-speed network connectivity, and reliable performance to ensure the algorithm&#8217;s optimal operation. Let us characterize a suitable infrastructure for a large-scale algorithm.</p><h3>Load balancing</h3><p>Load balancing is an essential practice in large-scale distributed computing algorithms. By evenly managing and distributing the workload, it prevents resource overload and maintains high system performance. It plays a significant role in ensuring efficient operations, optimal resource usage, and high throughput in the realm of distributed deep learning.</p><p><em>Figure 15.1</em> illustrates this concept visually. It shows a user interacting with a load balancer, which in turn manages the load on multiple nodes. In this case, there are four nodes, Node 1, Node 2, Node 3, and Node 4. The load balancer continually monitors the state of all nodes, distributing incoming user requests between them. The decision to assign a task to a specific node depends on the node&#8217;s current load and the load balancer&#8217;s algorithm. By preventing any single node from being overwhelmed while others remain underutilized, the load balancer ensures optimal system performance:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HyLL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdf7d0a-37c1-4fce-b66a-08c92c146e40_761x400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HyLL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdf7d0a-37c1-4fce-b66a-08c92c146e40_761x400.png 424w, https://substackcdn.com/image/fetch/$s_!HyLL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdf7d0a-37c1-4fce-b66a-08c92c146e40_761x400.png 848w, https://substackcdn.com/image/fetch/$s_!HyLL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdf7d0a-37c1-4fce-b66a-08c92c146e40_761x400.png 1272w, https://substackcdn.com/image/fetch/$s_!HyLL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdf7d0a-37c1-4fce-b66a-08c92c146e40_761x400.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HyLL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdf7d0a-37c1-4fce-b66a-08c92c146e40_761x400.png" width="761" height="400" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0cdf7d0a-37c1-4fce-b66a-08c92c146e40_761x400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:400,&quot;width&quot;:761,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Diagram\n\nDescription automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Diagram

Description automatically generated" title="Diagram

Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!HyLL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdf7d0a-37c1-4fce-b66a-08c92c146e40_761x400.png 424w, https://substackcdn.com/image/fetch/$s_!HyLL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdf7d0a-37c1-4fce-b66a-08c92c146e40_761x400.png 848w, https://substackcdn.com/image/fetch/$s_!HyLL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdf7d0a-37c1-4fce-b66a-08c92c146e40_761x400.png 1272w, https://substackcdn.com/image/fetch/$s_!HyLL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdf7d0a-37c1-4fce-b66a-08c92c146e40_761x400.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 15.1: Load balancing</em></p><p>In the broader context of cloud computing, AWS offers a feature called Elastic Load Balancing (ELB). ELB automatically distributes incoming application traffic across multiple targets within the AWS ecosystem, such as Amazon EC2 instances, IP addresses, or Lambda functions. By doing this, ELB prevents resource overload and maintains high application availability and performance.</p><h3>ELB: Combining elasticity and load balancing</h3><p>ELB represents an advanced technique that combines the elements of elasticity and load balancing into a single solution. It utilizes clusters of server groups to augment the responsiveness, efficiency, and scalability of computing infrastructure. The objective is to maintain a uniform distribution of workloads across all available resources, while simultaneously enabling the infrastructure to dynamically adjust its size in response to demand fluctuations.</p><p><em>Figure 15.2</em> shows a load balancer managing four server groups. Note that a server group is a collection of nodes tasked with specific computational functions. A server group here refers to an assembly of nodes, each given a unique computational role to fulfill.</p><p>One of the key features of a server group is its elasticity &#8211; its ability to flexibly add or remove nodes depending on the situation:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4PlA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a71985b-aa25-4819-8796-77add96201a6_825x338.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4PlA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a71985b-aa25-4819-8796-77add96201a6_825x338.png 424w, https://substackcdn.com/image/fetch/$s_!4PlA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a71985b-aa25-4819-8796-77add96201a6_825x338.png 848w, https://substackcdn.com/image/fetch/$s_!4PlA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a71985b-aa25-4819-8796-77add96201a6_825x338.png 1272w, https://substackcdn.com/image/fetch/$s_!4PlA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a71985b-aa25-4819-8796-77add96201a6_825x338.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4PlA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a71985b-aa25-4819-8796-77add96201a6_825x338.png" width="825" height="338" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a71985b-aa25-4819-8796-77add96201a6_825x338.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:338,&quot;width&quot;:825,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Graphical user interface, diagram\n\nDescription automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Graphical user interface, diagram

Description automatically generated" title="Graphical user interface, diagram

Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!4PlA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a71985b-aa25-4819-8796-77add96201a6_825x338.png 424w, https://substackcdn.com/image/fetch/$s_!4PlA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a71985b-aa25-4819-8796-77add96201a6_825x338.png 848w, https://substackcdn.com/image/fetch/$s_!4PlA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a71985b-aa25-4819-8796-77add96201a6_825x338.png 1272w, https://substackcdn.com/image/fetch/$s_!4PlA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a71985b-aa25-4819-8796-77add96201a6_825x338.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 15.2: Intelligent load balancing server autoscaling</em></p><p>Load balancers operate by continuously monitoring workload metrics in real time. When computational tasks become increasingly complex, the requirement for processing power correspondingly increases. To address this spike in demand, the system triggers a &#8220;scale-up&#8221; operation, integrating additional nodes into the existing server groups. In this context, &#8220;scaling up&#8221; is the process of increasing the computational capacity to accommodate the expanded workload. Conversely, when the demand decreases, the infrastructure can initiate a &#8220;scale-down&#8221; operation, in which some nodes are deallocated. This dynamic reallocation of nodes across server groups ensures an optimal resource utilization ratio. By adapting resource allocation to match the prevailing workload, the system prevents resource over-provisioning or under-provisioning. This dynamic resource management strategy results in an enhancement of operational efficiency and cost-effectiveness, while maintaining high-caliber performance.</p><h1>Strategizing multi-resource processing</h1><p>In the early days of strategizing multi-resource processing, large-scale algorithms were executed on powerful machines called supercomputers. These monolithic machines had a shared memory space, enabling quick communication between different processors and allowing them to access common variables through the same memory. As the demand for running large-scale algorithms grew, supercomputers transformed into Distributed Shared Memory (DSM) systems, where each processing node owned a segment of the physical memory. Subsequently, clusters emerged, constituting loosely connected systems that depend on message passing between processing nodes.</p><p>Effectively running large-scale algorithms requires multiple execution engines operating in parallel to tackle intricate challenges. Three primary strategies can be utilized to achieve this:</p><ul><li><p>Look within: Exploit the existing resources on a computer by using the hundreds of cores available on a GPU to execute large-scale algorithms. For instance, a data scientist aiming to train an intricate deep learning model could harness the GPU&#8217;s power to augment computing capabilities.</p></li><li><p>Look outside: Implement distributed computing to access supplementary computing resources that can collaboratively address large-scale issues. Examples include cluster computing and cloud computing, which enable running complex, resource-demanding algorithms by leveraging distributed resources.</p></li><li><p>Hybrid strategy: Merge distributed computing with GPU acceleration on each node to expedite algorithm execution. A scientific research organization processing vast amounts of data and conducting sophisticated simulations might adopt this approach. As illustrated in <em>Figure 15.3</em>, the computational workload is distributed across multiple nodes (Node 1, Node 2, and Node 3), each equipped with its own GPU. This figure effectively demonstrates the hybrid strategy, showcasing how simulations and computations are accelerated by capitalizing on the advantages of both distributed computing and GPU acceleration within each node:</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PRXG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eac8c69-1aa2-4889-ae46-01efbf39b27f_825x384.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PRXG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eac8c69-1aa2-4889-ae46-01efbf39b27f_825x384.png 424w, https://substackcdn.com/image/fetch/$s_!PRXG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eac8c69-1aa2-4889-ae46-01efbf39b27f_825x384.png 848w, https://substackcdn.com/image/fetch/$s_!PRXG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eac8c69-1aa2-4889-ae46-01efbf39b27f_825x384.png 1272w, https://substackcdn.com/image/fetch/$s_!PRXG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eac8c69-1aa2-4889-ae46-01efbf39b27f_825x384.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PRXG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eac8c69-1aa2-4889-ae46-01efbf39b27f_825x384.png" width="825" height="384" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8eac8c69-1aa2-4889-ae46-01efbf39b27f_825x384.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:384,&quot;width&quot;:825,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A screenshot of a computer\n\nDescription automatically generated with medium confidence&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A screenshot of a computer

Description automatically generated with medium confidence" title="A screenshot of a computer

Description automatically generated with medium confidence" srcset="https://substackcdn.com/image/fetch/$s_!PRXG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eac8c69-1aa2-4889-ae46-01efbf39b27f_825x384.png 424w, https://substackcdn.com/image/fetch/$s_!PRXG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eac8c69-1aa2-4889-ae46-01efbf39b27f_825x384.png 848w, https://substackcdn.com/image/fetch/$s_!PRXG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eac8c69-1aa2-4889-ae46-01efbf39b27f_825x384.png 1272w, https://substackcdn.com/image/fetch/$s_!PRXG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8eac8c69-1aa2-4889-ae46-01efbf39b27f_825x384.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 15.3: Hybrid strategy for multi-resource processing</em></p><p>As we explore the potential of parallel computing in running large-scale algorithms, it is equally important to understand the theoretical limitations that govern its efficiency.</p><p>In the following section, we will delve into the fundamental constraints of parallel computing, shedding light on the factors that influence its performance and the extent to which it can be optimized.</p><h1>Understanding theoretical limitations of parallel computing</h1><p>It is important to note that parallel algorithms are not a silver bullet. Even the best-designed parallel architectures may not give the performance that we may expect. The complexities of parallel computing, such as communication overhead and synchronization, make it challenging to achieve optimal efficiency. One law that has been developed to help navigate these complexities and better understand the potential gains and limitations of parallel algorithms is Amdahl&#8217;s law.</p><h2>Amdahl&#8217;s law</h2><p>Gene Amdahl was one of the first people to study parallel processing in the 1960s. He proposed Amdahl&#8217;s law, which is still applicable today and is a basis on which to understand the various trade-offs involved when designing a parallel computing solution. Amdahl&#8217;s law provides a theoretical limit on the maximum improvement in execution time that can be achieved with a parallelized version of an algorithm, given the proportion of the algorithm that can be parallelized.</p><p>It is based on the concept that in any computing process, not all processes can be executed in parallel. There will be a sequential portion of the process that cannot be parallelized.</p><h2>Deriving Amdahl&#8217;s law</h2><p>Consider an algorithm or task that can be divided into a parallelizable fraction (<em>f</em>) and a serial fraction (<em>1 - f</em>). The parallelizable fraction refers to the portion of the task that can be executed simultaneously across multiple resources or processors. These tasks don&#8217;t depend on each other and can be run in parallel, hence the term &#8220;parallelizable.&#8221; On the other hand, the serial fraction is part of the task that cannot be divided and must be executed in sequence, one after the other, hence &#8220;serial.&#8221;</p><p>Let <em>Tp(1)</em> represent the time required to process this task on a single processor. This can be expressed as:</p><p><em>T<sub>p</sub>(1) = N(1 - f)&#964;<sub>p</sub> + N(f)&#964;<sub>p</sub> = N&#964;<sub>p</sub></em></p><p>In these equations, <em>N</em> and <em>&#964;p</em> denote:</p><ul><li><p><em>N</em>: The total number of tasks or iterations that the algorithm or task must perform, consistent across both single and parallel processors.</p></li><li><p><em>&#964;<sub>p</sub></em>: The time taken by a processor to complete a single unit of work, task, or iteration, which remains constant regardless of the number of processors used.</p></li></ul><p>The preceding equation calculates the total time taken to process all tasks on a single processor. Now, let&#8217;s examine a scenario where the task is executed on <em>N</em> parallel processors.</p><p>The time taken for this execution can be represented as <em>T<sub>p</sub>(N)</em>. In the following diagram, on the X-axis, we have Number of processors. This represents the number of computing units or cores used to execute our program. As we move right along the X-axis, we are increasing the number of processors used. The Y-axis represents Speedup. This is a measure of how much faster our program runs with multiple processors compared to using just one. As we move up the Y-axis, the speed of our program increases proportionally, resulting in more efficient task execution.</p><p>The graph in <em>Figure 15.4</em> and Amdahl&#8217;s law show us that more processors can improve performance, but there&#8217;s a limit due to the sequential part of our code. This principle is a classic example of diminishing returns in parallel computing.</p><p><em>N = N(1 - f)&#964;<sub>p</sub> + (f)&#964;<sub>p</sub></em></p><p>Here, the first term on the RHS (Right Hand Side) represents the time taken to process the serial part of the task, while the second term denotes the time taken to process the parallel part.</p><p>The speedup in this case is due to the distribution of the parallel part of the task over <em>N</em> processors. Amdahl&#8217;s law defines the speedup <em>S(N)</em> achieved by using <em>N</em> processors as:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ab0K!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d4e465-9d0d-4606-bd4b-c7b107ee3d83_346x63.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ab0K!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d4e465-9d0d-4606-bd4b-c7b107ee3d83_346x63.png 424w, https://substackcdn.com/image/fetch/$s_!Ab0K!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d4e465-9d0d-4606-bd4b-c7b107ee3d83_346x63.png 848w, https://substackcdn.com/image/fetch/$s_!Ab0K!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d4e465-9d0d-4606-bd4b-c7b107ee3d83_346x63.png 1272w, https://substackcdn.com/image/fetch/$s_!Ab0K!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d4e465-9d0d-4606-bd4b-c7b107ee3d83_346x63.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ab0K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d4e465-9d0d-4606-bd4b-c7b107ee3d83_346x63.png" width="346" height="63" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/35d4e465-9d0d-4606-bd4b-c7b107ee3d83_346x63.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:63,&quot;width&quot;:346,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Ab0K!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d4e465-9d0d-4606-bd4b-c7b107ee3d83_346x63.png 424w, https://substackcdn.com/image/fetch/$s_!Ab0K!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d4e465-9d0d-4606-bd4b-c7b107ee3d83_346x63.png 848w, https://substackcdn.com/image/fetch/$s_!Ab0K!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d4e465-9d0d-4606-bd4b-c7b107ee3d83_346x63.png 1272w, https://substackcdn.com/image/fetch/$s_!Ab0K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35d4e465-9d0d-4606-bd4b-c7b107ee3d83_346x63.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>For significant speedup, the following condition must be satisfied:</p><p><em>1 - f &lt;&lt; f / N (4.4)</em></p><p>This inequality indicates that the parallel portion (<em>f</em>) must be very close to unity, especially when <em>N</em> is large.</p><p>Now, let&#8217;s look at a typical graph that explains Amdahl&#8217;s law:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VCaa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad3aff0e-4ead-4469-be2c-71493b306655_825x648.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VCaa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad3aff0e-4ead-4469-be2c-71493b306655_825x648.png 424w, https://substackcdn.com/image/fetch/$s_!VCaa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad3aff0e-4ead-4469-be2c-71493b306655_825x648.png 848w, https://substackcdn.com/image/fetch/$s_!VCaa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad3aff0e-4ead-4469-be2c-71493b306655_825x648.png 1272w, https://substackcdn.com/image/fetch/$s_!VCaa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad3aff0e-4ead-4469-be2c-71493b306655_825x648.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VCaa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad3aff0e-4ead-4469-be2c-71493b306655_825x648.png" width="825" height="648" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ad3aff0e-4ead-4469-be2c-71493b306655_825x648.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:648,&quot;width&quot;:825,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Chart\n\nDescription automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Chart

Description automatically generated" title="Chart

Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!VCaa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad3aff0e-4ead-4469-be2c-71493b306655_825x648.png 424w, https://substackcdn.com/image/fetch/$s_!VCaa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad3aff0e-4ead-4469-be2c-71493b306655_825x648.png 848w, https://substackcdn.com/image/fetch/$s_!VCaa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad3aff0e-4ead-4469-be2c-71493b306655_825x648.png 1272w, https://substackcdn.com/image/fetch/$s_!VCaa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad3aff0e-4ead-4469-be2c-71493b306655_825x648.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 15.4: Diminishing returns in parallel processing: visualizing Amdahl&#8217;s law</em></p><p>In <em>Figure 15.4</em>, the X-axis represents the number of processors (<em>N</em>), corresponding to the computing units or cores used to execute the program. As we move to the right along the X-axis, <em>N</em> increases. The Y-axis denotes the speedup (<em>S</em>), a measure of the improvement in the program&#8217;s execution time <em>T<sub>p</sub></em> with multiple processors compared to using just one. Moving up the Y-axis indicates an increase in the program&#8217;s execution speed.</p><p>The graph features four lines, each representing the speedup <em>S</em> obtained from parallel processing for different percentages of the parallelizable fraction (<em>f</em>): 50%, 75%, 90%, and 95%:</p><ul><li><p>50% parallel (<em>f = 0.5</em>): This line exhibits the smallest speedup <em>S</em>. Although more processors (<em>N</em>) are added, half of the program runs sequentially, limiting the speedup to a maximum of 2.</p></li><li><p>75% parallel (<em>f = 0.75</em>): The speedup <em>S</em> is higher compared to the 50% case. However, 25% of the program still runs sequentially, constraining the overall speedup.</p></li><li><p>90% parallel (<em>f = 0.9</em>): In this case, a significant speedup <em>S</em> is observed. Nevertheless, the sequential 10% of the program imposes a limit on the speedup.</p></li><li><p>95% parallel (<em>f = 0.95</em>): This line demonstrates the highest speedup <em>S</em>. Yet, the sequential 5% still imposes an upper limit on the speedup.</p></li></ul><p>The graph, in conjunction with Amdahl&#8217;s law, highlights that while increasing the number of processors (<em>N</em>) can enhance performance, there exists an inherent limit due to the sequential part of the code (<em>1 - f</em>). This principle serves as a classic illustration of diminishing returns in parallel computing.</p><p>Amdahl&#8217;s law provides valuable insights into the potential performance gains achievable in multiprocessor systems and the importance of the parallelizable fraction (<em>f</em>) in determining the system&#8217;s overall speedup. After discussing the theoretical limitations of parallel computing, it is crucial to introduce and explore another powerful and widely-used parallel processing technology: the GPU and its associated programming framework, CUDA.</p><h2>CUDA: Unleashing the potential of GPU architectures in parallel computing</h2><p>GPUs were originally designed for graphics processing but have since evolved, exhibiting distinct characteristics that set them apart from CPUs and resulting in an entirely different computation paradigm.</p><p>Unlike CPUs, which have a limited number of cores, GPUs are composed of thousands of cores. It&#8217;s essential to recognize, however, that these cores, in isolation, are not as individually powerful as a CPU core. But, GPUs are quite efficient at executing numerous relatively simple computations in parallel.</p><p>As GPUs were originally designed for graphic processing, GPU architecture is ideal for graphic processing where multiple operations can be executed independently. For example, rendering an image involves the computation of color and brightness for each pixel in the image. These calculations are largely independent of each other and hence can be conducted simultaneously, leveraging the multi-core architecture of the GPU.</p><h3>Bottom of form</h3><p>This design choice allows GPUs to be extremely efficient at tasks they&#8217;re designed for, like rendering graphics and processing large datasets. Here is the architecture of GPUs shown in <em>Figure 15.5</em>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tl33!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e554e0-9bef-40fd-b7d5-0fe21c05afb4_825x246.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tl33!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e554e0-9bef-40fd-b7d5-0fe21c05afb4_825x246.png 424w, https://substackcdn.com/image/fetch/$s_!tl33!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e554e0-9bef-40fd-b7d5-0fe21c05afb4_825x246.png 848w, https://substackcdn.com/image/fetch/$s_!tl33!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e554e0-9bef-40fd-b7d5-0fe21c05afb4_825x246.png 1272w, https://substackcdn.com/image/fetch/$s_!tl33!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e554e0-9bef-40fd-b7d5-0fe21c05afb4_825x246.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tl33!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e554e0-9bef-40fd-b7d5-0fe21c05afb4_825x246.png" width="825" height="246" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33e554e0-9bef-40fd-b7d5-0fe21c05afb4_825x246.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:246,&quot;width&quot;:825,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A picture containing table\n\nDescription automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A picture containing table

Description automatically generated" title="A picture containing table

Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!tl33!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e554e0-9bef-40fd-b7d5-0fe21c05afb4_825x246.png 424w, https://substackcdn.com/image/fetch/$s_!tl33!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e554e0-9bef-40fd-b7d5-0fe21c05afb4_825x246.png 848w, https://substackcdn.com/image/fetch/$s_!tl33!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e554e0-9bef-40fd-b7d5-0fe21c05afb4_825x246.png 1272w, https://substackcdn.com/image/fetch/$s_!tl33!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e554e0-9bef-40fd-b7d5-0fe21c05afb4_825x246.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 15.5: Architecture of GPUs</em></p><p>This unique architecture is not only beneficial for graphics processing but also significantly advantageous for other types of computational problems. Any problem that can be segmented into smaller, independent tasks can exploit this architecture for faster processing. This includes domains like scientific computing, machine learning, and even cryptocurrency mining, where massive datasets and complex computations are the norm.</p><p>Soon after GPUs became mainstream, data scientists started exploring them for their potential to efficiently perform parallel operations. As a typical GPU has thousands of Arithmetic Logic Units (ALUs), it has the potential to spawn thousands of concurrent processes. Note that the ALU is the workhorse of the core that performs most of the actual computations. This large number of ALUs makes GPUs well suited to tasks where the same operation needs to be performed on many data points simultaneously, such as vector and matrix operations common in data science and machine learning. Hence, algorithms that can perform parallel computations are best suited to GPUs. For example, an object search in a video is known to be at least 20 times faster in GPUs, compared to CPUs. Graph algorithms, which were discussed in <em>Chapter 5</em>, <em>Graph Algorithms</em>, are known to run much faster on GPUs than on CPUs.</p><p>In 2007, NVIDIA developed the open-source framework called Compute Unified Device Architecture (CUDA) to enable data scientists to harness the power of GPUs for their algorithms. CUDA abstracts the CPU and GPU as the host and the device, respectively.</p><p>Host refers to the CPU and main memory, responsible for executing the main program and offloading data-parallel computations to the GPU.</p><p>Device refers to the GPU and its memory (VRAM), responsible for executing kernels that perform data-parallel computations.</p><p>In a typical CUDA program, the host allocates memory on the device, transfers input data, and invokes a kernel. The device performs the computation, and the results are stored back in its memory. The host then retrieves the results. This division of labor leverages the strengths of each component, with the CPU handling complex logic and the GPU managing large-scale, data-parallel computations.</p><p>CUDA runs on NVIDIA GPUs and requires OS kernel support, initially starting with Linux and later extending to Windows. The CUDA Driver API bridges the programming language API and the CUDA driver, with support for C, C++, and Python.</p><h3>Parallel processing in LLMs: A case study in Amdahl&#8217;s law and diminishing returns</h3><p>LLMs, like ChatGPT, are intricate systems that generate text remarkably similar to human-written prose, given an initial prompt. This task involves a series of complex operations, which can be broadly divided into sequential and parallelizable tasks.</p><p>Sequential tasks are those that must occur in a specific order, one after the other. These tasks may include preprocessing steps like tokenizing, where the input text is broken down into smaller pieces, often words or phrases, which the model can understand. It may also encompass post-processing tasks like decoding, where the model&#8217;s output, often in the form of token probabilities, is translated back into human-readable text. These sequential tasks are critical to the function of the model, but by nature, they cannot be split up and run concurrently.</p><p>On the other hand, parallelizable tasks are those that can be broken down and run simultaneously. A key example of this is the forward propagation stage in the model&#8217;s neural network. Here, computations for each layer in the network can be performed concurrently. This operation constitutes the vast majority of the model&#8217;s computation time, and it is here where the power of parallel processing can be harnessed.</p><p>Now, assume that we&#8217;re working with a GPU that has 1,000 cores. In the context of language models, the parallelizable portion of the task might involve the forward propagation stage, where computations for each layer in the neural network can be performed concurrently. Let&#8217;s posit that this constitutes 95% of the total computation time. The remaining 5% of the task, which could involve operations like tokenizing and decoding, is sequential and cannot be parallelized.</p><p>Applying Amdahl&#8217;s law to this scenario gives us:</p><p><em>Speedup = 1 / ((1 - 0.95) + 0.95/1000) = 1 / (0.05 + 0.00095) = 19.61</em></p><p>Under ideal circumstances, this indicates that our language processing task could be completed about 19.61 times faster on a 1,000-core GPU than on a single-core CPU.</p><p>To further illustrate the diminishing returns of parallel computing, let&#8217;s adjust the number of cores to 2, 50, and 100:</p><ul><li><p>For 2 cores: <em>Speedup = 1 / ((1 - 0.95) + 0.95/2) = 1.67</em></p></li><li><p>For 50 cores: <em>Speedup = 1 / ((1 - 0.95) + 0.95/50) = 14.71</em></p></li><li><p>For 100 cores: <em>Speedup = 1 / ((1 - 0.95) + 0.95/100) = 16.81</em></p></li></ul><p>As seen from our calculations, adding more cores to a parallel computing setup doesn&#8217;t lead to an equivalent increase in speedup. This is a prime example of the concept of diminishing returns in parallel computing. Despite doubling the number of cores from 2 to 4, or increasing them 50-fold from 2 to 100, the speedup doesn&#8217;t double or increase 50 times. Instead, the speedup hits a theoretical limit as per Amdahl&#8217;s law.</p><p>The primary factor behind this diminishing return is the existence of a non-parallelizable portion in the task. In our case, operations like tokenizing and decoding form this sequential part, accounting for 5% of the total computation time. No matter how many cores we add to the system or how efficiently we can carry out the parallelizable part, this sequential portion places an upper limit on the achievable speedup. It will always be there, demanding its share of the computation time.</p><p>Amdahl&#8217;s law elegantly captures this characteristic of parallel computing. It states that the maximum potential speedup using parallel processing is dictated by the non-parallelizable part of the task. The law serves as a reminder to algorithm designers and system architects that while parallelism can dramatically speed up computation, it is not an infinite resource to be tapped for speed. It underscores the importance of identifying and optimizing the sequential components of an algorithm in order to maximize the benefits of parallel processing.</p><p>This understanding is particularly important in the context of LLMs, where the sheer scale of computations makes efficient resource utilization a key concern. It underlines the need for a balanced approach that combines parallel computing strategies with efforts to optimize the performance of the sequential parts of the task.</p><h3>Rethinking data locality</h3><p>Traditionally, in parallel and distributed processing, the data locality principle is pivotal in deciding the optimal resource allocation. It fundamentally suggests that the movement of data should be discouraged in distributed infrastructures. Whenever possible, instead of moving data, it should be processed locally on the node where it resides; otherwise, it reduces the benefit of parallelization and horizontal scaling, where horizontal scaling is the process of increasing a system&#8217;s capacity by adding more machines or nodes to distribute the workload, enabling it to handle higher amounts of traffic or data.</p><p>As networking bandwidth has improved over the years, the limitations imposed by data locality have become less significant. The increased data transfer speeds enable efficient communication between nodes in a distributed computing environment, reducing the reliance on data locality for performance optimization. The network bandwidth can be quantified by the network bisection bandwidth, which is the bandwidth between two equal parts of a network. This is important in distributed computing with resources that are physically distributed. If we draw a line somewhere between two sets of resources in a distributed network, the bisectional bandwidth is the rate of communication at which servers on one side of the line can communicate with servers on the other side, as shown in <em>Figure 15.6</em>. For distributed computing to work efficiently, this is the most important parameter to consider. If we do not have enough network bisection bandwidth, the benefits gained by the availability of multiple execution engines in distributed computing will be overshadowed by slow communication links.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GCEj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b7eb596-8579-4c95-aa2b-bd8a8a87eae8_429x228.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GCEj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b7eb596-8579-4c95-aa2b-bd8a8a87eae8_429x228.png 424w, https://substackcdn.com/image/fetch/$s_!GCEj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b7eb596-8579-4c95-aa2b-bd8a8a87eae8_429x228.png 848w, https://substackcdn.com/image/fetch/$s_!GCEj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b7eb596-8579-4c95-aa2b-bd8a8a87eae8_429x228.png 1272w, https://substackcdn.com/image/fetch/$s_!GCEj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b7eb596-8579-4c95-aa2b-bd8a8a87eae8_429x228.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GCEj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b7eb596-8579-4c95-aa2b-bd8a8a87eae8_429x228.png" width="429" height="228" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3b7eb596-8579-4c95-aa2b-bd8a8a87eae8_429x228.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:228,&quot;width&quot;:429,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A picture containing text\n\nDescription automatically generated&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A picture containing text

Description automatically generated" title="A picture containing text

Description automatically generated" srcset="https://substackcdn.com/image/fetch/$s_!GCEj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b7eb596-8579-4c95-aa2b-bd8a8a87eae8_429x228.png 424w, https://substackcdn.com/image/fetch/$s_!GCEj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b7eb596-8579-4c95-aa2b-bd8a8a87eae8_429x228.png 848w, https://substackcdn.com/image/fetch/$s_!GCEj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b7eb596-8579-4c95-aa2b-bd8a8a87eae8_429x228.png 1272w, https://substackcdn.com/image/fetch/$s_!GCEj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b7eb596-8579-4c95-aa2b-bd8a8a87eae8_429x228.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 15.6: Bisection bandwidth</em></p><p>A high bisectional bandwidth enables us to process the data where it is without copying it. These days, major cloud computing providers offer exceptional bisection bandwidth. For example, within a Google data center, the bisection bandwidth is as high as 1 petabyte per second. Other major Cloud vendors offer similar bandwidth. In contrast, a typical enterprise network might only provide bisection bandwidth in the range of 1 to 10 gigabytes per second.</p><p>This vast difference in speed demonstrates the remarkable capabilities of modern Cloud infrastructure, making it well suited to large-scale data processing tasks.</p><p>The increased petabit bisectional bandwidth has opened up new options and design patterns for efficiently storing and processing big data. These new options include alternative methods and design patterns that have become viable due to the increased network capacity, enabling faster and more efficient data processing.</p><h2>Benefiting from cluster computing using Apache Spark</h2><p>Apache Spark is a widely used platform for managing and leveraging cluster computing. In this context, &#8220;cluster computing&#8221; involves grouping together several machines and making them work together as a single system to solve a problem. Spark doesn&#8217;t merely implement this; it creates and controls these clusters to achieve high-speed data processing.</p><p>Within Apache Spark, data undergoes a transformation into what&#8217;s known as <strong>Resilient Distributed Datasets (RDDs)</strong>. These are effectively the backbone of Apache Spark&#8217;s data abstraction.</p><p>RDDs are immutable, meaning they can&#8217;t be altered once created, and are collections of elements that can be processed in parallel. In other words, different pieces of these datasets can be worked on at the same time, thereby accelerating data processing.</p><p>When we say &#8220;fault-tolerant,&#8221; we mean that RDDs have the ability to recover from potential failures or errors during execution. This makes them robust and reliable for big data processing tasks. They&#8217;re split into smaller chunks known as &#8220;partitions,&#8221; which are then distributed across various nodes or individual computers in the cluster. The size of these partitions can vary and is primarily determined by the nature of the task and the configuration of the Spark application.</p><p>Spark&#8217;s distributed computing framework allows the tasks to be distributed across multiple nodes, which can significantly improve processing speed and efficiency.</p><p>The Spark architecture is composed of several main components, including the driver program, executor, worker node, and cluster manager.</p><ul><li><p><strong>Driver program:</strong> The driver program is a key component in a Spark application, functioning much like the control center of operations. It resides in its own separate process, often located on a machine known as the driver machine. The driver program&#8217;s role is like that of a conductor of an orchestra; it runs the primary Spark program and oversees the many tasks within it.</p><p>Among the main tasks of the driver program are handling and running the SparkSession. The SparkSession is crucial to the Spark application as it wraps around the SparkContext. The SparkContext is like the central nervous system of the Spark application &#8211; it&#8217;s the gateway for the application to interact with the Spark computational ecosystem.</p><p></p><p>To simplify, imagine the Spark application as an office building. The driver program is like the building manager, responsible for overall operation and maintenance. Within this building, the SparkSession represents an individual office, and the SparkContext is the main entrance to that office. The essence is that these components &#8211; the driver program, SparkSession, and SparkContext &#8211; work together to coordinate tasks and manage resources within a Spark application. The SparkContext is packed with fundamental functions and context information that is pre-loaded at the start time of the application. Moreover, it carries vital details about the cluster, such as its configuration and status, which are crucial for the application to run and execute tasks effectively.</p></li></ul><ul><li><p><strong>Cluster manager:</strong> The driver program interacts seamlessly with the cluster manager. The cluster manager is an external service that provides and manages resources across the cluster, such as computing power and memory. The driver program and the cluster manager work hand in hand to identify available resources in the cluster, allocate them effectively, and manage their usage throughout the life cycle of the Spark application.</p></li><li><p><strong>Executors:</strong> An executor refers to a dedicated computational process that is spawned specifically for an individual Spark application running on a node within a cluster. Each of these executor processes operates on a worker node, effectively acting as the computational &#8220;muscle&#8221; behind your Spark application.</p><p></p><p>The sharing of both memory and global parameters in this way can significantly enhance the speed and efficiency of task execution, making Spark a highly performant framework for big data processing.</p></li><li><p><strong>Worker node:</strong> A worker node, true to its name, is charged with carrying out the actual execution of tasks within the distributed Spark system.</p><p>Each worker node is capable of hosting multiple executors, which in turn can serve numerous Spark applications:</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PB7r!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31655b96-52d9-4b04-80a1-90429e71ffb1_555x353.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PB7r!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31655b96-52d9-4b04-80a1-90429e71ffb1_555x353.png 424w, https://substackcdn.com/image/fetch/$s_!PB7r!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31655b96-52d9-4b04-80a1-90429e71ffb1_555x353.png 848w, https://substackcdn.com/image/fetch/$s_!PB7r!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31655b96-52d9-4b04-80a1-90429e71ffb1_555x353.png 1272w, https://substackcdn.com/image/fetch/$s_!PB7r!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31655b96-52d9-4b04-80a1-90429e71ffb1_555x353.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PB7r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31655b96-52d9-4b04-80a1-90429e71ffb1_555x353.png" width="555" height="353" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/31655b96-52d9-4b04-80a1-90429e71ffb1_555x353.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:353,&quot;width&quot;:555,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A diagram of a cluster manager\n\nDescription automatically generated with medium confidence&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A diagram of a cluster manager

Description automatically generated with medium confidence" title="A diagram of a cluster manager

Description automatically generated with medium confidence" srcset="https://substackcdn.com/image/fetch/$s_!PB7r!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31655b96-52d9-4b04-80a1-90429e71ffb1_555x353.png 424w, https://substackcdn.com/image/fetch/$s_!PB7r!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31655b96-52d9-4b04-80a1-90429e71ffb1_555x353.png 848w, https://substackcdn.com/image/fetch/$s_!PB7r!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31655b96-52d9-4b04-80a1-90429e71ffb1_555x353.png 1272w, https://substackcdn.com/image/fetch/$s_!PB7r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31655b96-52d9-4b04-80a1-90429e71ffb1_555x353.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 15.7: Spark&#8217;s distributed architecture</em></p><h1>How Apache Spark empowers large-scale algorithm processing</h1><p>Apache Spark has emerged as a leading platform for processing and analyzing big data, thanks to its powerful distributed computing capabilities, fault-tolerant nature, and ease of use. In this section, we will explore how Apache Spark empowers large-scale algorithm processing, making it an ideal choice for complex, resource-intensive tasks.</p><h2>Distributed computing</h2><p>At the core of Apache Spark&#8217;s architecture is the concept of data partitioning, which allows data to be divided across multiple nodes in a cluster. This feature enables parallel processing and efficient resource utilization, both of which are crucial for running large-scale algorithms. Spark&#8217;s architecture comprises a driver program and multiple executor processes distributed across worker nodes. The driver program is responsible for managing and distributing tasks across the executors, while each executor runs multiple tasks concurrently in separate threads, allowing for high throughput.</p><h2>In-memory processing</h2><p>One of Spark&#8217;s standout features is its in-memory processing capability. Unlike traditional disk-based systems, Spark can cache intermediate data in memory, significantly speeding up iterative algorithms that require multiple passes over the data.</p><p>This in-memory processing capability is particularly beneficial for large-scale algorithms, as it minimizes the time spent on disk I/O, leading to faster computation times and more efficient use of resources.</p><h1>Using large-scale algorithms in cloud computing</h1><p>The rapid growth of data and the increasing complexity of machine learning models have made distributed model training an essential component of modern deep learning pipelines. Large-scale algorithms demand vast amounts of computational resources and necessitate efficient parallelism to optimize their training times. Cloud computing offers an array of services and tools that facilitate distributed model training, allowing you to harness the full potential of resource-hungry, large-scale algorithms.</p><p>Some of the key advantages of using the Cloud for distributed model training include:</p><ul><li><p>Scalability: The Cloud provides virtually unlimited resources, allowing you to scale your model training workloads to meet the demands of large-scale algorithms.</p></li><li><p>Flexibility: The Cloud supports a wide range of machine learning frameworks and libraries, enabling you to choose the most suitable tools for your specific needs.</p></li><li><p>Cost-effectiveness: With the Cloud, you can optimize your training costs by selecting the right instance types and leveraging spot instances to reduce expenses.</p></li></ul><h2>Example</h2><p>As we delve deeper into machine learning models, especially those dealing with <strong>Natural Language Processing (NLP)</strong> tasks, we notice an increasing need for computational resources. For instance, transformers like GPT-3 for large-scale language modeling tasks can have billions of parameters, demanding substantial processing power and memory. Training such models on colossal datasets, such as Common Crawl, which contains billions of web pages, further escalates these requirements.</p><p>Cloud computing emerges as a potent solution here. It offers services and tools for distributed model training, enabling us to access an almost infinite pool of resources, scale our workloads, and select the most suitable machine learning frameworks. What&#8217;s more, cloud computing facilitates cost optimization by providing flexible instance types and spot instances &#8211; essentially bidding for spare computing capacity. By delegating these resource-heavy tasks to the cloud, we can concentrate more on innovative work, speeding up the training process, and developing more powerful models.</p><h1>Summary</h1><p>In this chapter, we examined the concepts and principles of large-scale and parallel algorithm design. The pivotal role of parallel computing was analyzed, with particular emphasis on its capacity to effectively distribute computational tasks across multiple processing units. The extraordinary capabilities of GPUs were studied in detail, illustrating their utility in executing numerous threads concurrently. Moreover, we discussed distributed computing platforms, specifically Apache Spark and cloud computing environments. Their importance in facilitating the development and deployment of large-scale algorithms was underscored, providing a robust, scalable, and cost-effective infrastructure for high-performance computations.</p><div><hr></div><p>To learn how to design and apply algorithms to solve real-world problems&#8212;from classical graph and sorting algorithms to cutting-edge techniques in deep learning, NLP, and large-scale systems&#8212;check out <em><strong><a href="https://www.packtpub.com/en-us/product/50-algorithms-every-programmer-should-know-9781803247762">50 Algorithms Every Programmer Should Know</a></strong></em> by Imran Ahmad, available from Packt. And keep an eye out for his upcoming book, <em><strong>30 Agents Every AI Engineer Should Know</strong></em>, publishing later this year.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.packtpub.com/en-us/product/50-algorithms-every-programmer-should-know-9781803247762" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VSoC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10c1bd15-b1a1-44d0-aaab-fc3034c5d163_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!VSoC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10c1bd15-b1a1-44d0-aaab-fc3034c5d163_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!VSoC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10c1bd15-b1a1-44d0-aaab-fc3034c5d163_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!VSoC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10c1bd15-b1a1-44d0-aaab-fc3034c5d163_2250x2775 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VSoC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10c1bd15-b1a1-44d0-aaab-fc3034c5d163_2250x2775" width="288" height="355.25274725274727" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10c1bd15-b1a1-44d0-aaab-fc3034c5d163_2250x2775&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:288,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;50 Algorithms Every Programmer Should Know&quot;,&quot;title&quot;:&quot;50 Algorithms Every Programmer Should Know&quot;,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.packtpub.com/en-us/product/50-algorithms-every-programmer-should-know-9781803247762&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="50 Algorithms Every Programmer Should Know" title="50 Algorithms Every Programmer Should Know" srcset="https://substackcdn.com/image/fetch/$s_!VSoC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10c1bd15-b1a1-44d0-aaab-fc3034c5d163_2250x2775 424w, https://substackcdn.com/image/fetch/$s_!VSoC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10c1bd15-b1a1-44d0-aaab-fc3034c5d163_2250x2775 848w, https://substackcdn.com/image/fetch/$s_!VSoC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10c1bd15-b1a1-44d0-aaab-fc3034c5d163_2250x2775 1272w, https://substackcdn.com/image/fetch/$s_!VSoC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10c1bd15-b1a1-44d0-aaab-fc3034c5d163_2250x2775 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here is what some readers have said:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6PyA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b2ddabb-e4a1-47e5-a87e-fe525edfd7ed_1076x522.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6PyA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b2ddabb-e4a1-47e5-a87e-fe525edfd7ed_1076x522.png 424w, https://substackcdn.com/image/fetch/$s_!6PyA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b2ddabb-e4a1-47e5-a87e-fe525edfd7ed_1076x522.png 848w, https://substackcdn.com/image/fetch/$s_!6PyA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b2ddabb-e4a1-47e5-a87e-fe525edfd7ed_1076x522.png 1272w, https://substackcdn.com/image/fetch/$s_!6PyA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b2ddabb-e4a1-47e5-a87e-fe525edfd7ed_1076x522.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6PyA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b2ddabb-e4a1-47e5-a87e-fe525edfd7ed_1076x522.png" width="1076" height="522" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2b2ddabb-e4a1-47e5-a87e-fe525edfd7ed_1076x522.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:522,&quot;width&quot;:1076,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:138182,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/166786809?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b2ddabb-e4a1-47e5-a87e-fe525edfd7ed_1076x522.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!6PyA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b2ddabb-e4a1-47e5-a87e-fe525edfd7ed_1076x522.png 424w, https://substackcdn.com/image/fetch/$s_!6PyA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b2ddabb-e4a1-47e5-a87e-fe525edfd7ed_1076x522.png 848w, https://substackcdn.com/image/fetch/$s_!6PyA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b2ddabb-e4a1-47e5-a87e-fe525edfd7ed_1076x522.png 1272w, https://substackcdn.com/image/fetch/$s_!6PyA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b2ddabb-e4a1-47e5-a87e-fe525edfd7ed_1076x522.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7Iid!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34bbbe4c-fdb3-43f3-9259-660483ec4868_1075x375.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7Iid!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34bbbe4c-fdb3-43f3-9259-660483ec4868_1075x375.png 424w, https://substackcdn.com/image/fetch/$s_!7Iid!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34bbbe4c-fdb3-43f3-9259-660483ec4868_1075x375.png 848w, https://substackcdn.com/image/fetch/$s_!7Iid!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34bbbe4c-fdb3-43f3-9259-660483ec4868_1075x375.png 1272w, https://substackcdn.com/image/fetch/$s_!7Iid!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34bbbe4c-fdb3-43f3-9259-660483ec4868_1075x375.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7Iid!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34bbbe4c-fdb3-43f3-9259-660483ec4868_1075x375.png" width="1075" height="375" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/34bbbe4c-fdb3-43f3-9259-660483ec4868_1075x375.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:375,&quot;width&quot;:1075,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:87821,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/166786809?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34bbbe4c-fdb3-43f3-9259-660483ec4868_1075x375.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!7Iid!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34bbbe4c-fdb3-43f3-9259-660483ec4868_1075x375.png 424w, https://substackcdn.com/image/fetch/$s_!7Iid!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34bbbe4c-fdb3-43f3-9259-660483ec4868_1075x375.png 848w, https://substackcdn.com/image/fetch/$s_!7Iid!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34bbbe4c-fdb3-43f3-9259-660483ec4868_1075x375.png 1272w, https://substackcdn.com/image/fetch/$s_!7Iid!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34bbbe4c-fdb3-43f3-9259-660483ec4868_1075x375.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SBK_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed6f1cb-60e5-48d2-a461-ccd9c0e6f7fc_1072x428.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SBK_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed6f1cb-60e5-48d2-a461-ccd9c0e6f7fc_1072x428.png 424w, https://substackcdn.com/image/fetch/$s_!SBK_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed6f1cb-60e5-48d2-a461-ccd9c0e6f7fc_1072x428.png 848w, https://substackcdn.com/image/fetch/$s_!SBK_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed6f1cb-60e5-48d2-a461-ccd9c0e6f7fc_1072x428.png 1272w, https://substackcdn.com/image/fetch/$s_!SBK_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed6f1cb-60e5-48d2-a461-ccd9c0e6f7fc_1072x428.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SBK_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed6f1cb-60e5-48d2-a461-ccd9c0e6f7fc_1072x428.png" width="1072" height="428" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3ed6f1cb-60e5-48d2-a461-ccd9c0e6f7fc_1072x428.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:428,&quot;width&quot;:1072,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:106008,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/166786809?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed6f1cb-60e5-48d2-a461-ccd9c0e6f7fc_1072x428.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!SBK_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed6f1cb-60e5-48d2-a461-ccd9c0e6f7fc_1072x428.png 424w, https://substackcdn.com/image/fetch/$s_!SBK_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed6f1cb-60e5-48d2-a461-ccd9c0e6f7fc_1072x428.png 848w, https://substackcdn.com/image/fetch/$s_!SBK_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed6f1cb-60e5-48d2-a461-ccd9c0e6f7fc_1072x428.png 1272w, https://substackcdn.com/image/fetch/$s_!SBK_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed6f1cb-60e5-48d2-a461-ccd9c0e6f7fc_1072x428.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>]]></content:encoded></item></channel></rss>