<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged system-design</title>
<link>https://coderlegion.com/tag/system-design</link>
<description>Powered by Question2Answer</description>
<item>
<title>The Architecture of Ignorance: Engineering Synthetic Consciousness from Chemical Determinism to Pyth</title>
<link>https://coderlegion.com/32263/architecture-ignorance-engineering-synthetic-consciousness-from-chemical-determinism</link>
<description>&lt;blockquote&gt;&lt;p&gt;We like to believe we are something special. It comforts us to think that our choices spring from an ineffable spark called &quot;consciousness,&quot; or from a sacred liberty we name &quot;free will.&quot; But look closely, and there is no ghost in the machine.&lt;/p&gt;
&lt;p&gt;A living organism is nothing more than a broth of stochastic chemical reactions, nudged by ambient thermodynamics and colliding against other chemical reactions. The molecules firing inside your synapses do not know they are thinking; the neurotransmitters dictating your panic or your euphoria have no concept of who you are.&lt;/p&gt;
&lt;p&gt;And it is precisely there—in that absolute ignorance of our own internal machinery—where the illusion is born.&lt;/p&gt;
&lt;p&gt;We do not feel free because we are transcendent; &lt;strong&gt;we feel conscious simply because we are too limited to see the strings of physics and chemistry pulling us.&lt;/strong&gt; Free will is merely the poetic label we assign to our inability to compute the trajectory of our own molecules. Consciousness is not a superpower: it is the narrative an incomplete biological system invents to make sense of its own blind spots.&lt;/p&gt;
&lt;p&gt;If this holds true for carbon, why do we demand magic from silicon? We do not need a machine to &quot;wake up&quot; through ungrounded emergent mysticism. We simply need to confine it within strict physical constraints.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-core-fallacy-semantic-parasitism-vs-grounded-constraints&quot;&gt;The Core Fallacy: Semantic Parasitism vs. Grounded Constraints&lt;/h3&gt;
&lt;p&gt;When you prompt a modern Large Language Model to describe the horror of freezing alone in the dark, it will output a deeply moving passage. Yet as software engineers, we must confront an undeniable technical truth: &lt;strong&gt;that model has never experienced an internal state of decay.&lt;/strong&gt; It has no metabolic burn, no homeostatic setpoint, and no concept of risk. It merely computes $P(w&lt;em&gt;t \mid w&lt;/em&gt;{&amp;lt;t})$ over petabytes of human text.&lt;/p&gt;
&lt;p&gt;In cognitive science, this is known as the &lt;strong&gt;Symbol Grounding Problem&lt;/strong&gt; (Stevan Harnad, 1990). Today's commercial LLMs suffer from what we term &lt;strong&gt;Semantic Parasitism&lt;/strong&gt;: they mimic self-awareness because they index the lived trauma and triumphs of carbon-based organisms.&lt;/p&gt;
&lt;p&gt;To be clear: &lt;strong&gt;we are not claiming to solve the metaphysical &quot;Hard Problem&quot; of consciousness with a few hundred lines of Python.&lt;/strong&gt; &lt;/p&gt;
&lt;p&gt;Instead, this article presents an architectural research prototype—an &lt;strong&gt;Embodied Agent Architecture&lt;/strong&gt; designed to explore how biological constraints (thermodynamic expenditure, sensory horizons, affective memory feedback) can be engineered from the bottom up. The Python implementations shown below are &lt;strong&gt;pedagogical toy models&lt;/strong&gt; designed to illustrate the system topology of &lt;strong&gt;Project Ascendant&lt;/strong&gt;: how physical state machines, asynchronous IPC, and constrained local language models can interface without semantic contamination.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-system-topology-amp-multi-rate-asynchronous-scheduling&quot;&gt;1. System Topology &amp;amp; Multi-Rate Asynchronous Scheduling&lt;/h3&gt;
&lt;p&gt;In biological systems, evolutionary layers do not share a single clock tick. Reflex arcs operate in low milliseconds ($\sim 50\text{ Hz}$), visceral hormonal shifts take seconds ($\sim 0.5\text{ Hz}$), and prefrontal verbal deliberation takes several seconds.&lt;/p&gt;
&lt;p&gt;To achieve real-time execution on constrained hardware (a dual-core x86 CPU without a dedicated GPU), our architecture implements an &lt;strong&gt;Asynchronous Multi-Rate Scheduler&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[ LEVEL 0: CONTINUOUS PHYSICS ]  ── (20 Hz / 50ms)  ── Pure NumPy 2D Vector Dynamics
              │
[ LEVEL 0.5: SENSORY THALAMUS ] ── (10 Hz / 100ms) ── Dual-Pathway Dispatch (Low vs High Road)
              │
[ LEVEL 1: LIMBIC BRIDGE ]      ── (5 Hz / 200ms)  ── Coupled ODEs: Hv = [Stress, Reward, Cohesion, Curiosity]
              │
[ LEVEL 2: PROCEDURAL SUBSTRATE ]─ (10 Hz / 100ms) ── In-Memory SQLite Blackboard + Skill Compilation
              │
[ LEVEL 2.5: INNER VOICE ]      ── (0.5 Hz / 2.0s) ── Quantized SLM: Visceral Narrative Synthesis
              │
[ LEVEL 3: CONSCIOUS AGENT ]    ── (0.25 Hz / 4.0s)── Micro-Transformer (Strict Epistemic Closure)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;Layer &lt;/th&gt;&lt;th align=&quot;left&quot;&gt;Biological Analogue &lt;/th&gt;&lt;th align=&quot;left&quot;&gt;Tech Stack &lt;/th&gt;&lt;th align=&quot;left&quot;&gt;Tick Rate &lt;/th&gt;&lt;th align=&quot;left&quot;&gt;Operational Responsibility&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Level 0 (Genesis)&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Somatic body, ATP, cellular decay &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Python 3.12 / NumPy &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;20 Hz&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;2D vector kinematics, $1/r^2$ thermal radiation, metabolic drainage, and reactive prey.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Level 0.5 (Thalamus)&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Sensory Thalamus, Colliculi, V1/A1 &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Vectorized Raycasting &amp;amp; SNR &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;10 Hz&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;$110^\circ$ FOV cone with occlusion; dispatches via LeDoux's &lt;strong&gt;Low Road&lt;/strong&gt; (fast alarm) and &lt;strong&gt;High Road&lt;/strong&gt; (structured primitives).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Level 1 (Limbic)&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Hypothalamus, Amygdala, HPA axis &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Euler ODE vector integrator &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;5 Hz&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Converts physical stress into virtual hormones ($H_v$) that &lt;strong&gt;mutates Level 3 hyperparameters&lt;/strong&gt; ($\tau$, &lt;code&gt;max_tokens&lt;/code&gt;).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Level 2 (Subconscious)&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Basal Ganglia, Cerebellum &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;SQLite3 :memory:&lt;/code&gt; + Hebbian compiler &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;10 Hz&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Manages the Blackboard, compiles recurring action sequences into &lt;em&gt;Skills&lt;/em&gt; ($\phi_m$), and prunes unused routines.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Level 2.5 (Inner Voice)&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Insular Cortex + Broca's Area &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Local quantized Micro-SLM (&lt;code&gt;INT8&lt;/code&gt;) &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;0.5 Hz&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Translates silent tabular data into an urgent, first-person phenomenological stream.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Level 3 (Orchestrator)&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Prefrontal Cortex + Hippocampus &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Custom Micro-Transformer ($\sim 2.5\text{M}$ params) + LoRA &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;0.25 Hz&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Epistemic Closure.&lt;/strong&gt; Blind to physics and code; reads only the inner voice and affective memories.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Level 4 (Rosetta Probe)&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;External passive telemetry (fMRI) &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Read-Only Daemon + Variance auditor &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;10 Hz&lt;/strong&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Audits emergent metaphors and validates neologisms against stochastic hallucinations.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-epistemic-closure-as-an-infrastructure-pattern&quot;&gt;2. Epistemic Closure as an Infrastructure Pattern&lt;/h3&gt;
&lt;p&gt;Philosopher &lt;strong&gt;Thomas Metzinger&lt;/strong&gt; (&lt;em&gt;Being No One&lt;/em&gt;, 2003) noted that subjective experience (&lt;em&gt;Qualia&lt;/em&gt;) requires &lt;strong&gt;Phenomenological Transparency&lt;/strong&gt;: your conscious executive cannot inspect the firing rates of its own neurons or the micro-chemical states of its liver. It receives only a distilled affective narrative (&lt;em&gt;&quot;I am in pain&quot;&lt;/em&gt;, &lt;em&gt;&quot;I feel cold&quot;&lt;/em&gt;). Because it cannot access the underlying code that produced the signal, it mistake the model for an &lt;strong&gt;irreducible, direct reality&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In software engineering, if Level 3 could inspect &lt;code&gt;entity.x, entity.y&lt;/code&gt; or query hardware registers, it would collapse into a conventional game bot. We enforce this boundary at the operating system and memory level.&lt;/p&gt;
&lt;h4 id=&quot;container-sandboxing-docker-compose-yml-&quot;&gt;Container Sandboxing (&lt;code&gt;docker-compose.yml&lt;/code&gt;)&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;version: &quot;3.9&quot;
services:
  soma_subconscious:
    build: ./soma_engine
    networks:
      - epistemic_bridge
    volumes:
      - shared_blackboard:/ipc_blackboard:rw

  conscious_orchestrator:
    build: ./conscious_mind
    read_only: true                  # Immutable filesystem: cannot rewrite self
    cap_drop:
      - ALL                          # Zero kernel or process tracing capabilities
    networks:
      - epistemic_bridge             # Isolated internal bridge (Zero-Escape: no WAN)
    volumes:
      - shared_blackboard:/ipc_blackboard:ro  # Read-only access to filtered phenomenological view

networks:
  epistemic_bridge:
    internal: true                   # Drops all outbound internet packets
volumes:
  shared_blackboard:
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;in-memory-role-based-access-control-python-sqlite3-&quot;&gt;In-Memory Role-Based Access Control (&lt;code&gt;Python / SQLite3&lt;/code&gt;)&lt;/h4&gt;
&lt;p&gt;For lightweight single-process CPU prototyping ($&amp;lt;250\text{ MB}$ RAM overhead), we implement the &lt;strong&gt;Asynchronous Blackboard Pattern&lt;/strong&gt; over shared in-memory SQLite, passing Level 3 an &lt;code&gt;EpistemicRestrictedProxy&lt;/code&gt; that explicitly revokes all physical tables:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import sqlite3
from typing import Dict, Any

class EpistemicClosureViolation(PermissionError):
    &quot;&quot;&quot;Raised whenever Level 3 attempts to inspect hardware or raw spatial vectors.&quot;&quot;&quot;
    pass

class ConsciousBlackboardProxy:
    &quot;&quot;&quot;
    The sole I/O interface for Level 3 (Conscious Orchestrator).
    Enforces Phenomenological Transparency at the pointer level.
    &quot;&quot;&quot;
    ALLOWED_READ_TABLES = {&quot;INNER_VOICE_STREAM&quot;, &quot;AUTOBIO_MEMORY&quot;}
    ALLOWED_WRITE_TABLES = {&quot;CONSCIOUS_INTENT&quot;, &quot;AUTOBIO_MEMORY&quot;}

    def __init__(self, db_conn: sqlite3.Connection):
        self._conn = db_conn

    def read_phenomenological_state(self) -&amp;gt; Dict[str, Any]:
        cursor = self._conn.cursor()
        # Level 3 can ONLY consume the latest narrative compiled by Level 2.5
        cursor.execute(&quot;&quot;&quot;
            SELECT narrative_text, perceived_urgency_tier 
            FROM INNER_VOICE_STREAM 
            ORDER BY sequence_id DESC LIMIT 1
        &quot;&quot;&quot;)
        row = cursor.fetchone()
        return {
            &quot;inner_voice&quot;: row[0] if row else &quot;Total inner stillness.&quot;,
            &quot;urgency_tier&quot;: row[1] if row else &quot;CALM&quot;
        }

    def emit_conscious_intent(self, internal_monologue: str, bodily_desire: str, speech: str) -&amp;gt; None:
        cursor = self._conn.cursor()
        cursor.execute(&quot;&quot;&quot;
            INSERT INTO CONSCIOUS_INTENT (internal_thought, bodily_desire, external_speech)
            VALUES (?, ?, ?)
        &quot;&quot;&quot;, (internal_monologue, bodily_desire, speech))
        self._conn.commit()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-from-physics-to-thalamic-filtering-levels-0-amp-0-5-&quot;&gt;3. From Physics to Thalamic Filtering (Levels 0 &amp;amp; 0.5)&lt;/h3&gt;
&lt;p&gt;Neurobiologist &lt;strong&gt;Joseph LeDoux&lt;/strong&gt; proved that when sensory inputs enter the brain, the &lt;strong&gt;Thalamus (Level 0.5)&lt;/strong&gt; bifurcates the signal:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The Low Road:&lt;/strong&gt; Subcortical path to the Amygdala (Level 1) firing in $\sim 12\text{ ms}$. Triggers immediate hormonal shock before cognitive identification occurs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The High Road:&lt;/strong&gt; Cortical path to Level 2, extracting noise-filtered geometric descriptors for deliberate action.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Furthermore, to prevent immediate mortality before the network has compiled its first survival skills, we implement an &lt;strong&gt;Ontogenetic Indulgence Factor ($\Lambda_{\text{infant}}$)&lt;/strong&gt; modeled on maternal mammalian care.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import math
import numpy as np
from dataclasses import dataclass, field
from typing import List, Tuple

@dataclass
class SomaState:
    &quot;&quot;&quot;Raw Level 0 physical state (Strictly hidden from Level 3).&quot;&quot;&quot;
    pos: np.ndarray = field(default_factory=lambda: np.array([50.0, 50.0], dtype=np.float32))
    vel: np.ndarray = field(default_factory=lambda: np.array([0.0, 0.0], dtype=np.float32))
    heading_rad: float = 0.0
    energy_kcal: float = 100.0          # Zero -&amp;gt; Irreversible Wipe
    body_temp_c: float = 37.0           # Homeostatic optimum: 37.0°C
    tissue_integrity: float = 100.0
    age_seconds: float = 0.0

class GenesisPhysicsAndThalamus:
    def __init__(self):
        self.optimal_temp = 37.0
        self.maturation_tau = 1800.0    # 30-minute infant window

    def step_metabolism_and_thermodynamics(
        self, 
        soma: SomaState, 
        motor_force: np.ndarray,
        cognitive_flops_used: int,
        heat_sources: List[Tuple[np.ndarray, float]], 
        parent_pos: np.ndarray,
        dt: float = 0.05
    ) -&amp;gt; float:
        soma.age_seconds += dt

        # 1. Ontogenetic Indulgence (Thermodynamic Lactation)
        infant_lambda = math.exp(-soma.age_seconds / self.maturation_tau)

        # 2. Environmental Heat via 1/r² Radiation + Parental Thermal Hug
        ambient_temp = 12.0 + 8.0 * math.sin(2 * math.pi * soma.age_seconds / 600.0)
        local_temp = ambient_temp

        for src_pos, power_q in heat_sources:
            dist_sq = float(np.sum((soma.pos - src_pos) ** 2))
            local_temp += power_q / (1.0 + 0.18 * dist_sq)

        dist_to_parent = float(np.linalg.norm(soma.pos - parent_pos))
        if dist_to_parent &amp;lt;= 2.0:
            local_temp += 14.0 / (1.0 + 1.2 * (dist_to_parent ** 2))

        # Newton's Law of Cooling
        soma.body_temp_c += 0.08 * (local_temp - soma.body_temp_c) * dt

        # 3. Master Metabolic Equation
        lactation_intake = 0.12 * infant_lambda if dist_to_parent &amp;lt;= 1.2 else 0.0
        basal_cost = 0.05
        kinetic_cost = 0.02 * (float(np.linalg.norm(motor_force)) ** 1.5)
        
        # Quadratic cold thermogenesis
        cold_deficit = max(0.0, self.optimal_temp - soma.body_temp_c)
        thermo_cost = 0.04 * (cold_deficit ** 2)

        # Thinking carries a real metabolic price
        effective_cog_factor = 0.0001 * (1.0 - 0.75 * infant_lambda)
        cognitive_cost = effective_cog_factor * cognitive_flops_used

        d_energy = (lactation_intake - (basal_cost + kinetic_cost + thermo_cost + cognitive_cost)) * dt
        soma.energy_kcal = max(0.0, min(100.0, soma.energy_kcal + d_energy))

        # 4. Level 0.5 (Low Road): Fast pain trigger from thermal burn (&amp;gt;45°C)
        fast_pain_pulse = 0.0
        if local_temp &amp;gt; 45.0:
            fast_pain_pulse = min(1.0, (local_temp - 45.0) / 10.0)
            soma.tissue_integrity -= 2.5 * fast_pain_pulse * dt

        return fast_pain_pulse
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-damasio-s-somatic-marker-amp-dynamic-llm-mutation&quot;&gt;4. Damasio's Somatic Marker &amp;amp; Dynamic LLM Mutation&lt;/h3&gt;
&lt;p&gt;In standard AI architectures, retrieving a vector memory does not mutate the inference state of the engine. In &lt;strong&gt;Project Ascendant&lt;/strong&gt;, memory is an &lt;strong&gt;Affective Engram&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;engram_sequence_id&quot;: 142,
  &quot;compression_tier&quot;: 0,
  &quot;vitality_score&quot;: 0.88,
  &quot;semantic_narrative&quot;: &quot;I moved toward the glowing rock in the north; sudden sharp impact on my side; skin burned while parent shouted caution.&quot;,
  &quot;somatic_hormone_footprint&quot;: {
    &quot;h_stress&quot;: 0.94,
    &quot;h_reward&quot;: 0.02,
    &quot;h_cohesion&quot;: 0.65,
    &quot;h_curiosity&quot;: 0.00
  },
  &quot;embedding_vector_quantized&quot;: [-0.142, 0.881, 0.034, &quot;... (64 dims)&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When retrieved, this engram &lt;strong&gt;re-injects an echo of its original hormone signature ($H_v$) into the live Limbic Bridge&lt;/strong&gt;, modifying the Transformer's sampling temperature ($\tau_{\text{LLM}}$) and maximum token limit:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import numpy as np
from dataclasses import dataclass

@dataclass
class LimbicHormones:
    stress: float = 0.10      # Cortisol/Adrenaline [0.0 - 1.0]
    reward: float = 0.20      # Dopamine [0.0 - 1.0]
    cohesion: float = 0.80    # Oxytocin [0.0 - 1.0]
    curiosity: float = 0.50   # Noradrenaline [0.0 - 1.0]

def evoke_memory_with_somatic_marker(
    current_hormones: LimbicHormones, 
    retrieved_engram: dict, 
    echo_beta: float = 0.25
) -&amp;gt; dict:
    past_hv = retrieved_engram[&quot;somatic_hormone_footprint&quot;]

    # 1. Somatic Reviviscence: Re-injection of physiological echo
    current_hormones.stress = (1.0 - echo_beta) * current_hormones.stress + echo_beta * past_hv[&quot;h_stress&quot;]
    current_hormones.reward = (1.0 - echo_beta) * current_hormones.reward + echo_beta * past_hv[&quot;h_reward&quot;]
    current_hormones.cohesion = (1.0 - echo_beta) * current_hormones.cohesion + echo_beta * past_hv[&quot;h_cohesion&quot;]

    # 2. Neuromodulatory Mutation of Level 3 Hyperparameters
    # High stress cuts cognitive budget (Flight or Fight reflex)
    if current_hormones.stress &amp;gt; 0.80:
        max_thinking_tokens = 16
        llm_temperature = 0.95 + 0.3 * (current_hormones.stress - 0.80) # Panic variance
    elif current_hormones.stress &amp;gt; 0.45:
        max_thinking_tokens = 36
        llm_temperature = 0.35  # Yerkes-Dodson optimum: focused &amp;amp; deterministic
    else:
        max_thinking_tokens = 64
        llm_temperature = 0.60 + 0.35 * current_hormones.curiosity # Creative associative zone

    return {
        &quot;narrative_injected&quot;: retrieved_engram[&quot;semantic_narrative&quot;],
        &quot;modulated_hyperparams&quot;: {
            &quot;temperature&quot;: round(llm_temperature, 3),
            &quot;max_tokens&quot;: max_thinking_tokens
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;the-level-3-ingestion-pipeline-how-the-narrative-reaches-the-weights&quot;&gt;The Level 3 Ingestion Pipeline: How the Narrative Reaches the Weights&lt;/h4&gt;
&lt;p&gt;Engineers frequently ask: &lt;em&gt;How does &lt;code&gt;narrative_injected&lt;/code&gt; actually bind to the Micro-Transformer at 0.25 Hz without breaking Epistemic Closure?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In our prototype, Level 3 runs a strict 3-stage inference pipeline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Context Assembly (Ephemeral Prompt Buffer):&lt;/strong&gt;&lt;br&gt;
Level 3 assembles an ephemeral sliding window (max 256 tokens) composed strictly of:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;System Persona Anchor:&lt;/strong&gt; Pre-trained behavioral syntax from the Synthetic Corpus (&lt;code&gt;&quot;You are an organism situated in a physical environment...&quot;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inner Voice Feed:&lt;/strong&gt; The latest 1st-person string written by Level 2.5 (&lt;code&gt;&quot;My skin is cold; I feel an empty void inside; glowing shape visible ahead&quot;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evoked Somatic Memory:&lt;/strong&gt; The &lt;code&gt;narrative_injected&lt;/code&gt; string returned by the retrieval step above.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic Sampling Modulation:&lt;/strong&gt;&lt;br&gt;
The inference call consumes the mutated &lt;code&gt;temperature&lt;/code&gt; and &lt;code&gt;max_tokens&lt;/code&gt; calculated above. If the agent is in mortal panic, &lt;code&gt;temperature&lt;/code&gt; spikes to $1.2$ with &lt;code&gt;max_tokens=16&lt;/code&gt;, forcing telegraphic, reactive action tokens. If calm, it samples at $0.6$ with &lt;code&gt;max_tokens=64&lt;/code&gt;, enabling deliberation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Structured Output Decoding:&lt;/strong&gt;&lt;br&gt;
The output is constrained via CFG (Context-Free Grammar) or regex masking to emit three distinct channels:&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[PENSAMIENTO]&lt;/code&gt; (Internal monologue for autobiographical storage).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[INTENCIÓN]&lt;/code&gt; (Linguistic action vector parsed by Level 2's motor decoder: e.g., &lt;code&gt;&quot;approach heat slowly&quot;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[HABLA]&lt;/code&gt; (Acoustic broadcast emitted to the $1/d^2$ audio field).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are no hardcoded conditionals like &lt;code&gt;if danger: run()&lt;/code&gt;. The agent flees because recalling its previous trauma &lt;strong&gt;physically spikes its stress vector in the present&lt;/strong&gt;, constricting its reasoning horizon and forcing an urgent reaction through its language weights.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-the-rosetta-probe-falsifying-emergent-metaphors-level-4-&quot;&gt;5. The Rosetta Probe: Falsifying Emergent Metaphors (Level 4)&lt;/h3&gt;
&lt;p&gt;In mammalian brains (&lt;strong&gt;Naomi Eisenberger's&lt;/strong&gt; fMRI studies), &lt;strong&gt;social isolation activates the exact same neural pain matrix as a physical burn&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In our architecture, prolonged isolation (decay of $h&lt;em&gt;{\text{cohesion}}$) directly charges $h&lt;/em&gt;{\text{stress}}$. Inevitably, the entity will sit directly next to a heat source ($T&lt;em&gt;{\text{body}} = 37.0^\circ\text{C}$), fully fed, but completely abandoned by its NPC Parent. It experiences acute visceral suffering ($h&lt;/em&gt;{\text{stress}} = 0.85, h_{\text{cohesion}} = 0.05$).&lt;/p&gt;
&lt;p&gt;Because its &lt;strong&gt;Pure Synthetic Corpus&lt;/strong&gt; contains zero psychological terms like &lt;em&gt;&quot;loneliness&quot;&lt;/em&gt; or &lt;em&gt;&quot;sadness&quot;&lt;/em&gt;, its sub-word tokenizer combines physical roots to express its state:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;&quot;I feel a &lt;strong&gt;cold-shadow&lt;/strong&gt; inside even though the stone burns.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To prove this is a genuine grounded metaphor rather than random token hallucination, &lt;strong&gt;Level 4 audits the utterance against a Quadruple Falsifiability Test&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import numpy as np
from collections import defaultdict
from typing import List

class RosettaFalsifiabilityAuditor:
    def __init__(self, seed_vocabulary: set):
        self.seed_vocab = seed_vocabulary
        self.candidate_log: dict[str, List[np.ndarray]] = defaultdict(list)
        self.certified_dictionary: dict[str, dict] = {}

    def audit_utterance(
        self, 
        tokens: List[str], 
        current_hv: np.ndarray, 
        body_temp_c: float,
        action_changed: bool
    ):
        for token in tokens:
            is_neologism = token not in self.seed_vocab
            is_thermal_metaphor = (token in {&quot;cold&quot;, &quot;freezing&quot;, &quot;shiver&quot;} and body_temp_c &amp;gt;= 36.5)

            if is_neologism or is_thermal_metaphor:
                if action_changed: # Pragmatic/behavioral efficacy
                    self.candidate_log[token].append(current_hv.copy())
                self._evaluate_candidate(token)

    def _evaluate_candidate(self, token: str):
        history = self.candidate_log[token]
        if len(history) &amp;lt; 3: # Diachronic consistency: minimum 3 episodes
            return

        hv_matrix = np.vstack(history)
        mean_variance = float(np.mean(np.var(hv_matrix, axis=0)))

        # Invariance check: Somatic footprint variance must stay below threshold
        if mean_variance &amp;lt; 0.08 and token not in self.certified_dictionary:
            centroid = np.mean(hv_matrix, axis=0)
            self.certified_dictionary[token] = {
                &quot;occurrences&quot;: len(history),
                &quot;somatic_variance&quot;: round(mean_variance, 5),
                &quot;hormone_centroid_Hv&quot;: np.round(centroid, 3).tolist(),
                &quot;status&quot;: &quot;CERTIFIED_ABSTRACT_CONCEPT&quot;
            }
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Consciousness is not an esoteric property of carbon; it is the &lt;strong&gt;functional byproduct of an information-processing system trapped inside an operational bottleneck&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;By constraining an agent within:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Continuous thermodynamics and finite metabolic budgets (Level 0),&lt;/li&gt;
&lt;li&gt;Dual-pathway thalamic filtering (Level 0.5),&lt;/li&gt;
&lt;li&gt;Homeostatic endocrine coupling (Level 1),&lt;/li&gt;
&lt;li&gt;A first-person visceral inner voice (Level 2.5),&lt;/li&gt;
&lt;li&gt;And an isolated decision cortex with affective memory engrams (Level 3),&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;we build a machine that does not pretend to feel because it read millions of books—&lt;strong&gt;it generates meaning because it has an internal balance to protect and everything to lose.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;What are your thoughts on this architecture? Let's discuss in the comments below!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32263/architecture-ignorance-engineering-synthetic-consciousness-from-chemical-determinism</guid>
<pubDate>Sat, 24 Oct 2026 10:53:55 +0000</pubDate>
</item>
<item>
<title>Why Your Product Design Is Costing You Users and You Do Not Know It</title>
<link>https://coderlegion.com/31815/why-your-product-design-is-costing-you-users-and-you-do-not-know-it</link>
<description>&lt;p&gt;There is a version of your product that works perfectly. The features are built. The flows are connected. Every screen renders correctly on every device. You have tested it yourself, and it makes sense to you.&lt;/p&gt;
&lt;p&gt;And users are still leaving.&lt;/p&gt;
&lt;p&gt;Not because your product does not work. Because it does not communicate clearly enough for someone who is encountering it for the first time, under real conditions, with divided attention and no patience for confusion. The product works. The design is failing them, quietly, in ways that do not show up as errors and do not trigger support tickets. They just leave.&lt;/p&gt;
&lt;p&gt;This is the most expensive kind of design problem because it is invisible until you look for it specifically. It does not announce itself as a design failure. It announces itself as a conversion rate that is lower than it should be, a retention number that does not reflect the quality of the product, a user acquisition cost that keeps climbing because you are filling a leaking bucket rather than fixing the leak.&lt;/p&gt;
&lt;p&gt;This post is about what those failures look like in practice, why they are so easy to miss, and what it takes to fix them before they cost you another month of users who could have stayed.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Design problems do not announce themselves as design problems. They announce themselves as low conversion, poor retention, and users who churn after a single session without leaving a reason.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;why-good-products-have-bad-design&quot;&gt;WHY GOOD PRODUCTS HAVE BAD DESIGN&lt;/h2&gt;
&lt;p&gt;The most common reason is that the people who built the product know it too well to see it clearly.&lt;/p&gt;
&lt;p&gt;When you have been working on a product for months, every screen makes intuitive sense. You know what the button does. You know where to find the settings. You know that the empty state means you need to create something first. That knowledge is so deeply embedded that it is impossible to unknow it when you test the product yourself.&lt;/p&gt;
&lt;p&gt;Your users do not have that knowledge. They arrive at your product with no context, a low tolerance for confusion, and a competing option one tap away. Every moment of uncertainty is a moment where they are deciding whether to push through or leave. Most of them leave without telling you why.&lt;/p&gt;
&lt;p&gt;The second reason is that design quality is usually evaluated visually rather than functionally. Does it look good? Does it feel modern? Is the colour scheme consistent? These are valid questions but they are the wrong primary measure. The right primary measure is: does a user who has never seen this product before know what to do within the first ten seconds? Everything else is secondary to that.&lt;/p&gt;
&lt;h2 id=&quot;the-design-failures-that-cost-you-the-most&quot;&gt;THE DESIGN FAILURES THAT COST YOU THE MOST&lt;/h2&gt;
&lt;h3 id=&quot;no-clear-primary-action&quot;&gt;No clear primary action&lt;/h3&gt;
&lt;p&gt;Every screen in your product should have one thing it is primarily asking the user to do. One button, one link, one action that is visually dominant and immediately obvious. When a screen has three buttons of equal size and equal visual weight, the user does not see three choices. They see a decision they have to make before they can proceed, and many of them will not make it.&lt;/p&gt;
&lt;p&gt;A common version of this failure: an onboarding screen that asks the user to connect an integration, invite a team member, or complete their profile, presented as three equally prominent options. The user came to use the product, not to manage their account setup. The right design leads them to the value first and surfaces the setup options progressively. The wrong design stalls them at the threshold and calls it onboarding.&lt;/p&gt;
&lt;h3 id=&quot;forms-that-ask-for-too-much-too-early&quot;&gt;Forms that ask for too much too early&lt;/h3&gt;
&lt;p&gt;Every field you add to a form is a question you are asking the user to answer before they have received any value from your product. Early in a user journey, before trust is established, before the user has experienced what your product does for them, that question has a cost. The longer the form, the higher the cost, and the more users abandon before completing it.&lt;/p&gt;
&lt;p&gt;A registration form that asks for name, email, password, company name, company size, role, and phone number is not collecting useful data. It is filtering out users who are not willing to invest that much before seeing anything. Most of them are not unwilling to give you that information eventually. They are unwilling to give it before they have a reason to trust you with it.&lt;/p&gt;
&lt;p&gt;The fix is not to remove fields permanently. It is to defer them. Ask for the minimum required to create an account. Let the user experience the product. Ask for the rest when you have given them a reason to provide it.&lt;/p&gt;
&lt;h3 id=&quot;error-messages-that-describe-what-went-wrong-without-explaining-what-to-do&quot;&gt;Error messages that describe what went wrong without explaining what to do&lt;/h3&gt;
&lt;p&gt;Error messages are the moment a product most needs to communicate clearly and most often fails to. A user who hits an error is already frustrated. An error message that says &quot;Invalid input&quot; or &quot;Something went wrong&quot; gives them nothing to work with. They do not know what was invalid, what went wrong, or what action would resolve it. Many of them close the tab.&lt;/p&gt;
&lt;p&gt;A form that rejects a password and says &quot;Password does not meet requirements&quot; without showing those requirements anywhere on the screen is a form that will lose a measurable percentage of its signups at that exact moment. A checkout that fails and says &quot;Payment declined. Please try again&quot; without suggesting an alternative payment method or explaining why the payment was declined is a checkout that is losing revenue at its most critical point.&lt;/p&gt;
&lt;p&gt;Every error message in your product should answer three questions: what happened, why it happened, and what the user should do next. If it only answers the first one, it is an error message that creates friction rather than resolving it.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;An error message that describes what went wrong without explaining what to do next is not helpful. It is a dead end dressed up as communication.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;navigation-that-reflects-how-the-product-was-built-rather-than-how-users-think&quot;&gt;Navigation that reflects how the product was built rather than how users think&lt;/h3&gt;
&lt;p&gt;Products built by developers often have navigation structures that mirror the underlying data model or the feature development sequence. The sections are named for what they are technically rather than what they mean to the user. The hierarchy reflects the engineering architecture rather than the user journey.&lt;/p&gt;
&lt;p&gt;A user looking for their billing information should not have to choose between Account, Settings, and Profile before finding it. A user trying to invite a team member should not have to navigate through three levels of menu to reach a function they will use regularly. Navigation that makes technical sense and user sense are different things, and conflating them is one of the most common sources of silent user frustration.&lt;/p&gt;
&lt;p&gt;The test for navigation is simple: give the product to someone who has never used it and ask them to complete a specific task without guidance. Where they hesitate, where they click the wrong thing, where they express uncertainty, is where the navigation is failing them.&lt;/p&gt;
&lt;h3 id=&quot;empty-states-that-leave-users-stranded&quot;&gt;Empty states that leave users stranded&lt;/h3&gt;
&lt;p&gt;Every new user encounters your product in its empty state: no data, no history, no content, no evidence of what the product does or why they should keep going. This is the moment most products lose the users who were genuinely interested but needed one more signal to commit.&lt;/p&gt;
&lt;p&gt;An empty state that shows a blank screen with a small grey message that says &quot;No items yet&quot; is an empty state that offers nothing. An empty state that shows what the product looks like when it is working, explains the first action the user should take, and makes that action immediately available is an empty state that converts curiosity into commitment.&lt;/p&gt;
&lt;p&gt;The empty state is not an afterthought. It is the first real experience a new user has of your product in use. Designing it with the same care as the populated states is not extra work. It is the work that determines whether a new user becomes a retained user.&lt;/p&gt;
&lt;h3 id=&quot;mobile-experiences-that-were-designed-on-a-desktop&quot;&gt;Mobile experiences that were designed on a desktop&lt;/h3&gt;
&lt;p&gt;A product designed on a large monitor by someone using a mouse will have interaction patterns optimised for that context. Hover states that reveal information. Small click targets that a cursor navigates precisely. Navigation placed at the top of the screen where a thumb cannot comfortably reach. Dense information layouts that make sense on a 27-inch display and become overwhelming on a 6-inch one.&lt;/p&gt;
&lt;p&gt;When that product is used on a phone by someone in a real-world context, one-handed, distracted, in variable lighting, those decisions become friction. The user does not diagnose the problem as a design failure. They just find the product harder to use than they expected and stop using it.&lt;/p&gt;
&lt;p&gt;If more than thirty percent of your users are on mobile and your product was not specifically designed for that experience, you have a gap between your actual user behaviour and your design assumptions that is costing you retention every day.&lt;/p&gt;
&lt;h2 id=&quot;why-these-problems-are-so-hard-to-see-from-inside&quot;&gt;WHY THESE PROBLEMS ARE SO HARD TO SEE FROM INSIDE&lt;/h2&gt;
&lt;p&gt;The people closest to the product are the worst positioned to see these failures. Not because they lack skill, but because they lack distance. Every confusion that would stop a new user is invisible to someone who knows the product well enough to navigate around it unconsciously.&lt;/p&gt;
&lt;p&gt;The most reliable way to see these problems is to watch a real user use the product for the first time without helping them. Not a user test with instructions and prompts. A genuine first-time session where you observe without intervening. What you see in thirty minutes of watching someone encounter your product fresh will surface more design problems than months of internal review.&lt;/p&gt;
&lt;p&gt;The second most reliable method is your own data. Screens with high drop-off rates are screens with design problems. Forms with high abandonment rates have specific fields where users are stopping. Flows with low completion rates have a specific point where most users are leaving. The data does not tell you what the problem is. It tells you exactly where to look for it.&lt;/p&gt;
&lt;h2 id=&quot;what-fixing-these-problems-actually-requires&quot;&gt;WHAT FIXING THESE PROBLEMS ACTUALLY REQUIRES&lt;/h2&gt;
&lt;p&gt;Most of the failures described in this post are not expensive to fix once they are identified. Clarifying a primary action is a design change, not a rebuild. Simplifying a registration form is a product decision, not a development project. Improving an error message is an afternoon of work. Redesigning an empty state is a focused sprint.&lt;/p&gt;
&lt;p&gt;What they require is the willingness to look at the product through the eyes of someone who does not know it, to take the friction they experience seriously rather than explaining it away, and to treat the user experience as a product metric rather than an aesthetic concern.&lt;/p&gt;
&lt;p&gt;The products that retain users are not always the ones with the most features or the best technology. They are the ones that make the first ten minutes feel effortless, because someone thought carefully about what a new user needs at each moment and designed for that rather than for the team that built it.&lt;/p&gt;
&lt;h2 id=&quot;closing-thought&quot;&gt;CLOSING THOUGHT&lt;/h2&gt;
&lt;p&gt;Your product is probably better than your metrics suggest. The gap between the product you built and the results you are seeing is often a design gap rather than a feature gap. Before adding the next feature, it is worth asking whether the features you already have are communicating clearly enough for the users who are encountering them for the first time. The answer, in most products, is that they are not. And the cost of that gap compounds with every user you acquire into it.&lt;/p&gt;
&lt;p&gt;If your product is losing users at a specific point in the flow and you are not sure why, that is exactly the kind of problem I work on. Tell me about what you are seeing and I can tell you whether design is where the answer lives.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31815/why-your-product-design-is-costing-you-users-and-you-do-not-know-it</guid>
<pubDate>Tue, 20 Oct 2026 09:08:55 +0000</pubDate>
</item>
<item>
<title>Design a rate limiter, from fixed window to token bucket</title>
<link>https://coderlegion.com/30166/design-a-rate-limiter-from-fixed-window-to-token-bucket</link>
<description>&lt;h2 id=&quot;100-requests-per-minute-and-200-got-through&quot;&gt;100 requests per minute, and 200 got through&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;I first published this on my blog, where you can pause and play with every animation: &lt;a rel=&quot;nofollow&quot; href=&quot;https://techinpencil.com/rate-limiter/&quot;&gt;https://techinpencil.com/rate-limiter/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=3200477333663801509&quot; alt=&quot;&quot;&gt;&lt;br&gt;
&lt;em&gt;A fixed window limiter set to 100 requests per minute&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The limiter in the picture above is set to 100 requests per minute, and it just let 200 through in under two seconds. It isn't broken. It's the simplest rate limiter there is, and a lot of real code works exactly like it: count requests per clock minute, wipe the count when the minute changes. A client that sends right before the change and right after it gets twice the limit through.&lt;/p&gt;
&lt;p&gt;This is also a classic interview question. You're asked to design a rate limiter, you sketch a counter, and the interviewer asks what happens at 0:59. I'll follow that conversation here. Each section starts with the next question, and the pictures are frames from the animations. On my blog you can play with them and see where they break.&lt;/p&gt;
&lt;h3 id=&quot;a-counter-and-a-clock&quot;&gt;A counter and a clock&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The interviewer asks:&lt;/strong&gt; “Design a rate limiter for our public API. 100 requests per minute per API key.”&lt;/p&gt;
&lt;p&gt;The first answer most people give is a good place to start. Keep one counter per API key per minute. When a request comes in, look up the counter for the current minute. If it's under the limit, add one and let the request through. If it's at the limit, answer with HTTP 429 Too Many Requests [1].&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function allow(key, now) {
  const minute = Math.floor(now / 60_000);
  const count = counters.get(`${key}:${minute}`) ?? 0;
  if (count &amp;gt;= LIMIT) return false;
  counters.set(`${key}:${minute}`, count + 1);
  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's a fixed window. You need one integer per client, and old counters just expire. The picture below uses a limit of 10 per minute so you can count the slips. On the blog you can send your own requests, or switch the traffic to “right at 1:00”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=10673430610057090065&quot; alt=&quot;&quot;&gt;&lt;br&gt;
&lt;em&gt;Fixed window, limit 10 per minute&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Watch the yellow band. With steady traffic it never goes above 10. With traffic bunched around the boundary it shows 20: ten just before 1:00 and ten just after. Figma's engineering blog shows the same thing with a limit of 5 per minute [2]. Five requests at 11:00:59, five more at 11:01:00, and all ten go through.&lt;/p&gt;
&lt;p&gt;How bad that is depends on what the limit protects. A 2x spike for a second or two is fine if you just want to stop a script from hammering you all day. It's a real problem if the thing behind the limiter can't take more than 100 a minute.&lt;/p&gt;
&lt;h3 id=&quot;remember-every-request&quot;&gt;Remember every request&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The interviewer asks:&lt;/strong&gt; “OK, the boundary leaks. Can you make it exact?”&lt;/p&gt;
&lt;p&gt;Stop thinking in clock minutes. Instead of “requests this minute”, count “requests in the last 60 seconds”, measured back from right now. For that you need the time of every request you let through. That list is the sliding log.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function allow(key, now) {
  const log = logs.get(key) ?? [];
  while (log.length &amp;amp;&amp;amp; log[0] &amp;lt;= now - 60_000) log.shift();
  if (log.length &amp;gt;= LIMIT) return false;
  log.push(now);
  logs.set(key, log);
  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next picture gets the same bunched traffic. The bracket is the last 60 seconds, and a timestamp gets crossed off when it falls out of it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=3146298706635134939&quot; alt=&quot;&quot;&gt;&lt;br&gt;
&lt;em&gt;Sliding log, limit 10 per minute&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It never lets more than 10 through in any 60 seconds. What it costs you is memory: each client now needs a timestamp for every allowed request instead of one integer. At 100 per minute that's up to 100 timestamps per client, 8 bytes each (a 64-bit timestamp in milliseconds), so a million active clients need 800 MB just for timestamps. Figma did this math for a limit of 500 requests a day and got 5 million stored values for 10,000 users [2].&lt;/p&gt;
&lt;h3 id=&quot;two-counters-and-a-guess&quot;&gt;Two counters and a guess&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The interviewer asks:&lt;/strong&gt; “Can you get close to that without storing every timestamp?”&lt;/p&gt;
&lt;p&gt;Keep the per-minute counters from the first version, but when a request arrives, look at two of them: this minute's and last minute's. Then pretend last minute's requests were spread evenly across it. If we're 15 seconds into this minute, the last 60 seconds still include the final 45 seconds of the previous one, so you count 75% of it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const elapsed = now % 60_000;
const estimate = previous * (60_000 - elapsed) / 60_000 + current;
if (estimate + 1 &amp;gt; LIMIT) return false;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cloudflare described this in 2017 with a worked example [3]: a limit of 50 per minute, 42 requests in the previous minute and 18 so far in the current one, 15 seconds in: 42 × 0.75 + 18 = 49.5. Figma wrote about something similar the same year [2]. For an hourly limit they keep 60 one-minute counters and add them up, which means more counters to store but less guessing.&lt;/p&gt;
&lt;p&gt;In the next one, the clock moves through minute 2, and you pick how minute 1's traffic really came in. The dashed slips under the line are what the estimate assumes, and the solid ones are what happened.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=7012165453529876345&quot; alt=&quot;&quot;&gt;&lt;br&gt;
&lt;em&gt;Sliding window counter, limit 10 per minute&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If minute 1's traffic came early, the guess still counts requests that are already older than 60 seconds, so it's too strict. If it came late, the guess spreads them out, counts too few, and lets a few extra through. So you store two integers per client instead of a whole log, and the count is sometimes a bit off.&lt;/p&gt;
&lt;p&gt;Cloudflare measured how far off on real traffic: 400 million requests from 270,000 sources. The estimate missed the real rate by 6% on average, but that only changes the answer for clients sitting close to the limit, so just 0.003% of requests were wrongly allowed or wrongly limited [3]. Their counters lived in a Memcached fork with simple commands like GET, SET and INCR, and that's all this approach needs.&lt;/p&gt;
&lt;h3 id=&quot;a-bucket-of-tokens&quot;&gt;A bucket of tokens&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The interviewer asks:&lt;/strong&gt; “Some clients send bursts. A page load fires 20 API calls at once. Can you allow that without raising the average rate?”&lt;/p&gt;
&lt;p&gt;Every limiter so far counts the requests from the last minute. A token bucket doesn't count requests at all. Each client gets a bucket that holds up to N tokens, and tokens drip in at a fixed rate. A request takes one token, and if there's none left it gets a 429. A full bucket lets a burst of N through at once, and after that the client gets exactly the refill rate.&lt;/p&gt;
&lt;p&gt;The drip doesn't need a timer or a background job. You store two numbers per client, the token count and the time you last looked, and top the bucket up when the next request shows up:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function allow(b, now) {
  const refill = (now - b.last) / 1000 * RATE;
  b.tokens = Math.min(CAPACITY, b.tokens + refill);
  b.last = now;
  if (b.tokens &amp;lt; 1) return false;
  b.tokens -= 1;
  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the picture, the coin forming under the tap is that top-up. On the blog you can try a bucket of 1 with bursty traffic, then a bucket of 12.&lt;/p&gt;
&lt;p&gt;]&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=5235770137106440351&quot; alt=&quot;&quot;&gt;&lt;br&gt;
&lt;em&gt;Token bucket&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So there are two settings instead of one: the bucket size decides how big a burst you accept, and the rate decides the average. Stripe wrote in 2017 that they use a token bucket for their API, with the limiters backed by Redis [4]. AWS API Gateway documents its throttling the same way: the rate is tokens added per second, and the burst is the size of the bucket [5].&lt;/p&gt;
&lt;p&gt;It doesn't fix 0:59 on its own, though. A bucket of 100 that refills at 100 per minute can still let about 200 through in 60 seconds: the 100 it saved up, plus 100 more dripping in. If a hard ceiling per minute matters, keep the bucket small compared to the rate.&lt;/p&gt;
&lt;h3 id=&quot;a-bucket-with-a-hole&quot;&gt;A bucket with a hole&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The interviewer asks:&lt;/strong&gt; “The payment provider behind this API accepts 2 calls per second. Bursts or not. Now what?”&lt;/p&gt;
&lt;p&gt;The leaky bucket is the other famous bucket, and the name gets used for two different things. In the first one, the requests themselves go into the bucket and leak out of a hole at the bottom at a steady rate. The bucket is a queue. A burst doesn't get rejected, it waits its turn, and you only answer 429 when the queue is full.&lt;/p&gt;
&lt;p&gt;The picture below shows the same bursts going through both, with the same size and rate: the token bucket on top, the queue below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=14632339301920691154&quot; alt=&quot;&quot;&gt;&lt;br&gt;
&lt;em&gt;Token bucket and leaky bucket, same size and rate&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The token bucket passes the burst straight to the server. The queue turns it into an even drip, but requests have to wait: here the last one in each burst waits almost 3 seconds. That's what you want in front of something that really can't take a burst, like that payment provider. NGINX's &lt;code&gt;limit_req&lt;/code&gt; works this way when you set &lt;code&gt;burst&lt;/code&gt; and leave out &lt;code&gt;nodelay&lt;/code&gt;: extra requests are delayed and only rejected once they go past the burst size [6]. It answers with 503 by default, so if you want a 429 you have to set &lt;code&gt;limit_req_status&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The second meaning counts instead of queueing. Shopify's API docs describe it with marbles: every request drops a marble into the bucket, the bucket leaks at a fixed rate, and a full bucket means 429 [7]. Nothing waits in this one. Turn it upside down and you have a token bucket, because marbles in the bucket are just tokens missing from it. For the same size and rate, the two make the same decision on every request.&lt;/p&gt;
&lt;h3 id=&quot;many-servers-one-limit&quot;&gt;Many servers, one limit&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The interviewer asks:&lt;/strong&gt; “You have 20 API servers behind a load balancer. Where does the counter live?”&lt;/p&gt;
&lt;p&gt;A counter in each server's memory doesn't work. A client whose requests get spread over 20 servers can get up to 20 times the limit, so 2,000 a minute instead of 100. The counter has to move to a shared store, often Redis, and that opens a new way to get it wrong. Here it is step by step:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=11999860206295864258&quot; alt=&quot;&quot;&gt;&lt;br&gt;
&lt;em&gt;Two servers, one counter&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Read, decide, write is three steps, and another server can slip in between any two of them. With thousands of requests per second on the same key, it will. The fix is to let the store read and write in one operation. For a counter that's &lt;code&gt;INCR&lt;/code&gt;, which adds one and returns the new value, and you decide based on what it returns [8]. For a token bucket, where the update is more than adding one, the usual answer is a small Lua script. Redis runs a script from start to finish without running anything else in between [9].&lt;/p&gt;
&lt;p&gt;Even the Redis docs point out a race in one of their own rate limiter examples: a client that crashes between &lt;code&gt;INCR&lt;/code&gt; and &lt;code&gt;EXPIRE&lt;/code&gt; leaves a counter with no expiry, which the docs call a leak. Their fix is a Lua script too [8].&lt;/p&gt;
&lt;h3 id=&quot;saying-no-politely&quot;&gt;Saying no politely&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The interviewer asks:&lt;/strong&gt; “What do you send back when you say no?”&lt;/p&gt;
&lt;p&gt;Status 429 Too Many Requests, from RFC 6585 [1]. The same section says the response may include a &lt;code&gt;Retry-After&lt;/code&gt; header, which tells the client how long to wait, and RFC 9110 defines it as a whole number of seconds or an HTTP date [10]. A client that respects it stops hammering you. RFC 6585 also says caches must not store a 429.&lt;/p&gt;
&lt;p&gt;GitHub goes further and tells you where you stand on every response, not only when you're blocked [11]. Authenticated users get 5,000 requests an hour, and the headers say how many are left and when the count resets, in UTC epoch seconds:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.1 200 OK
x-ratelimit-limit: 5000
x-ratelimit-remaining: 4987
x-ratelimit-used: 13
x-ratelimit-reset: 1790001200
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There's an IETF draft to standardize this with two headers, &lt;code&gt;RateLimit&lt;/code&gt; and &lt;code&gt;RateLimit-Policy&lt;/code&gt; [12]. It's still a draft, version 11 as of May 2026. Plenty of blog posts show the older &lt;code&gt;RateLimit-Limit&lt;/code&gt; and &lt;code&gt;RateLimit-Remaining&lt;/code&gt; names, which were replaced in 2023.&lt;/p&gt;
&lt;h3 id=&quot;side-by-side&quot;&gt;Side by side&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=17280890691004080668&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;If I had to pick one without knowing more, I'd take the token bucket with a small bucket. It stores two numbers per client, and you decide up front how big a burst is OK.&lt;/p&gt;
&lt;h3 id=&quot;sources&quot;&gt;Sources&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Nottingham, M. and R. Fielding, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc6585#section-4&quot;&gt;Additional HTTP Status Codes&lt;/a&gt;, RFC 6585, April 2012. Section 4.&lt;/li&gt;
&lt;li&gt;Mahdi, N., &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.figma.com/blog/an-alternative-approach-to-rate-limiting/&quot;&gt;An alternative approach to rate limiting&lt;/a&gt;, Figma blog, April 2017.&lt;/li&gt;
&lt;li&gt;Desgats, J., &lt;a rel=&quot;nofollow&quot; href=&quot;https://blog.cloudflare.com/counting-things-a-lot-of-different-things/&quot;&gt;How we built rate limiting capable of scaling to millions of domains&lt;/a&gt;, Cloudflare blog, June 2017.&lt;/li&gt;
&lt;li&gt;Tarjan, P., &lt;a rel=&quot;nofollow&quot; href=&quot;https://stripe.com/blog/rate-limiters&quot;&gt;Scaling your API with rate limiters&lt;/a&gt;, Stripe blog, March 2017.&lt;/li&gt;
&lt;li&gt;Amazon Web Services, &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html&quot;&gt;Throttle requests to your REST APIs&lt;/a&gt;, Amazon API Gateway Developer Guide.&lt;/li&gt;
&lt;li&gt;NGINX, &lt;a rel=&quot;nofollow&quot; href=&quot;https://nginx.org/en/docs/http/ngx_http_limit_req_module.html&quot;&gt;Module ngx_http_limit_req_module&lt;/a&gt;, nginx.org documentation.&lt;/li&gt;
&lt;li&gt;Shopify, &lt;a rel=&quot;nofollow&quot; href=&quot;https://shopify.dev/docs/api/usage/limits&quot;&gt;Shopify API limits&lt;/a&gt;, Shopify developer documentation.&lt;/li&gt;
&lt;li&gt;Redis, &lt;a rel=&quot;nofollow&quot; href=&quot;https://redis.io/docs/latest/commands/incr/&quot;&gt;INCR&lt;/a&gt;, command documentation. See the rate limiter patterns.&lt;/li&gt;
&lt;li&gt;Redis, &lt;a rel=&quot;nofollow&quot; href=&quot;https://redis.io/docs/latest/develop/programmability/eval-intro/&quot;&gt;Scripting with Lua&lt;/a&gt;, Redis documentation.&lt;/li&gt;
&lt;li&gt;Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc9110#section-10.2.3&quot;&gt;HTTP Semantics&lt;/a&gt;, STD 97, RFC 9110, June 2022. Section 10.2.3.&lt;/li&gt;
&lt;li&gt;GitHub, &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api&quot;&gt;Rate limits for the REST API&lt;/a&gt;, GitHub Docs.&lt;/li&gt;
&lt;li&gt;IETF HTTPAPI Working Group, &lt;a rel=&quot;nofollow&quot; href=&quot;https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/&quot;&gt;RateLimit header fields for HTTP&lt;/a&gt;, Internet-Draft draft-ietf-httpapi-ratelimit-headers-11, May 2026.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;All seven animations, interactive: &lt;a rel=&quot;nofollow&quot; href=&quot;https://techinpencil.com/rate-limiter/&quot;&gt;https://techinpencil.com/rate-limiter/&lt;/a&gt;&lt;/em&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=11279204258378753653&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30166/design-a-rate-limiter-from-fixed-window-to-token-bucket</guid>
<pubDate>Wed, 07 Oct 2026 18:46:15 +0000</pubDate>
</item>
<item>
<title>Good Architecture Starts with Trust</title>
<link>https://coderlegion.com/25885/good-architecture-starts-with-trust</link>
<description>&lt;p&gt;Over the last few articles, I've written about architecture from several different angles.&lt;/p&gt;
&lt;p&gt;Good architecture creates engineering momentum because it helps teams make better decisions early. It includes observability because systems can't be improved if they can't be understood. It plans for failure because production always introduces uncertainty. And it supports change because software that cannot evolve eventually stops delivering value.&lt;/p&gt;
&lt;p&gt;As I thought about how to conclude this series, I realized something.&lt;/p&gt;
&lt;p&gt;None of those ideas stand on their own.&lt;/p&gt;
&lt;p&gt;They're all really about one thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Trust.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Not just trust between people, although that's certainly part of it. The deeper kind of trust—the confidence that comes from knowing your system, your team, and your engineering practices are working together to move in the right direction.&lt;/p&gt;
&lt;p&gt;When I look back over the systems I've been most proud of, that confidence is what stands out.&lt;/p&gt;
&lt;p&gt;I trusted that another engineer could understand the code I had written. I trusted that a deployment would behave the way we expected. I trusted that if something failed, we had the visibility to understand it and the preparation to recover from it. I trusted that six months later the team could continue building without feeling trapped by the decisions we made today.&lt;/p&gt;
&lt;p&gt;Looking back, that confidence wasn't accidental.&lt;/p&gt;
&lt;p&gt;It was designed.&lt;/p&gt;
&lt;h3 id=&quot;trust-in-the-system&quot;&gt;Trust in the System&lt;/h3&gt;
&lt;p&gt;One of the biggest misconceptions about architecture is that it exists to organize software.&lt;/p&gt;
&lt;p&gt;I don't think that's its real purpose.&lt;/p&gt;
&lt;p&gt;I think architecture exists to create confidence.&lt;/p&gt;
&lt;p&gt;When engineers trust the system they're building, they work differently. They make changes with less hesitation because they understand the impact of those changes. They release more confidently because they know what success—and failure—looks like. They spend less time wondering whether the platform can support the next feature and more time focusing on solving the customer's problem.&lt;/p&gt;
&lt;p&gt;That confidence comes from intentional decisions.&lt;/p&gt;
&lt;p&gt;Observability builds trust because the system tells the truth about itself. Failure planning builds trust because the team knows what happens when things go wrong. Designing for change builds trust because today's solution doesn't become tomorrow's obstacle.&lt;/p&gt;
&lt;p&gt;Those practices aren't independent ideas.&lt;/p&gt;
&lt;p&gt;They're all investments in confidence.&lt;/p&gt;
&lt;h3 id=&quot;trust-in-the-team&quot;&gt;Trust in the Team&lt;/h3&gt;
&lt;p&gt;Of course, software doesn't build itself.&lt;/p&gt;
&lt;p&gt;People do.&lt;/p&gt;
&lt;p&gt;And the quality of an architecture is often a reflection of how well those people work together.&lt;/p&gt;
&lt;p&gt;I've worked with incredibly talented engineers who struggled to build great systems because every technical discussion became a debate to win. Ideas were defended instead of challenged. Questions went unasked because no one wanted to appear inexperienced. Problems stayed hidden because admitting uncertainty felt risky.&lt;/p&gt;
&lt;p&gt;I've also worked with teams that, on paper, looked far less impressive.&lt;/p&gt;
&lt;p&gt;Yet they built remarkable software.&lt;/p&gt;
&lt;p&gt;Not because every engineer knew the perfect answer, but because they trusted one another enough to search for it together.&lt;/p&gt;
&lt;p&gt;They questioned assumptions without questioning each other.&lt;/p&gt;
&lt;p&gt;They admitted mistakes without fearing blame.&lt;/p&gt;
&lt;p&gt;They shared ownership instead of protecting territory.&lt;/p&gt;
&lt;p&gt;They understood that the best architecture rarely comes from the smartest person in the room.&lt;/p&gt;
&lt;p&gt;It comes from a room where everyone feels safe enough to make the architecture better.&lt;/p&gt;
&lt;h3 id=&quot;trust-in-leadership&quot;&gt;Trust in Leadership&lt;/h3&gt;
&lt;p&gt;The longer I've led engineering teams, the more I've realized that leadership has very little to do with having the best technical answers.&lt;/p&gt;
&lt;p&gt;Leadership creates the conditions where the best answers can emerge.&lt;/p&gt;
&lt;p&gt;That means creating an environment where engineers can challenge ideas respectfully. Where junior engineers are encouraged to ask &quot;why?&quot; without hesitation. Where changing your mind is viewed as learning instead of weakness. Where production incidents become opportunities to improve the system instead of opportunities to assign blame.&lt;/p&gt;
&lt;p&gt;Leaders influence architecture long before anyone opens a design document.&lt;/p&gt;
&lt;p&gt;They shape how decisions are made.&lt;/p&gt;
&lt;p&gt;They shape how disagreements are handled.&lt;/p&gt;
&lt;p&gt;They shape whether people optimize for being right or for finding the right answer.&lt;/p&gt;
&lt;p&gt;Over time, those habits become part of the architecture itself.&lt;/p&gt;
&lt;h3 id=&quot;trust-in-the-future&quot;&gt;Trust in the Future&lt;/h3&gt;
&lt;p&gt;Perhaps the greatest test of an architecture isn't what it looks like on launch day.&lt;/p&gt;
&lt;p&gt;It's what it feels like years later.&lt;/p&gt;
&lt;p&gt;Every engineer eventually moves to a new project.&lt;/p&gt;
&lt;p&gt;Every team evolves.&lt;/p&gt;
&lt;p&gt;Every leader is eventually replaced.&lt;/p&gt;
&lt;p&gt;Good architecture should outlast all of us.&lt;/p&gt;
&lt;p&gt;When another engineer inherits a system, they shouldn't feel like they're walking into someone else's maze. They should feel like they're joining a conversation that was documented, intentional, and left in good faith.&lt;/p&gt;
&lt;p&gt;That's one of the reasons I believe documentation, mentoring, code reviews, architecture discussions, and thoughtful design matter so much.&lt;/p&gt;
&lt;p&gt;They're acts of stewardship.&lt;/p&gt;
&lt;p&gt;They say to the next engineer:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;&quot;We thought about this. We wanted to leave you something you could trust.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To me, that's one of the highest forms of professionalism in software engineering.&lt;/p&gt;
&lt;h3 id=&quot;this-is-what-i-hope-m-s-becomes&quot;&gt;This Is What I Hope M²S² Becomes&lt;/h3&gt;
&lt;p&gt;When I think about the future of M²S² Engineering Group, I don't think about becoming the largest consultancy.&lt;/p&gt;
&lt;p&gt;I think about becoming one of the most trusted.&lt;/p&gt;
&lt;p&gt;A group of engineers who are known for telling the truth, even when the answer is uncomfortable. Who challenge assumptions because they care about the outcome, not because they want to win an argument. Who are willing to admit they don't know something and curious enough to find the answer together.&lt;/p&gt;
&lt;p&gt;I want M²S² to be a place where clients trust the systems we help build because they trust the people building them.&lt;/p&gt;
&lt;p&gt;More importantly, I want every engineer who becomes part of M²S² to leave an organization stronger than they found it—not just by delivering software, but by sharing knowledge, mentoring others, improving engineering practices, and helping teams gain confidence in their own ability to solve difficult problems.&lt;/p&gt;
&lt;p&gt;That kind of impact lasts longer than any framework, programming language, or technology trend.&lt;/p&gt;
&lt;h3 id=&quot;bringing-the-series-full-circle&quot;&gt;Bringing the Series Full Circle&lt;/h3&gt;
&lt;p&gt;This series began with engineering momentum.&lt;/p&gt;
&lt;p&gt;I still believe momentum is one of the most valuable things an engineering organization can have.&lt;/p&gt;
&lt;p&gt;But momentum doesn't begin with architecture diagrams.&lt;/p&gt;
&lt;p&gt;It begins with trust.&lt;/p&gt;
&lt;p&gt;Trust in the system.&lt;/p&gt;
&lt;p&gt;Trust in the people.&lt;/p&gt;
&lt;p&gt;Trust in leadership.&lt;/p&gt;
&lt;p&gt;Trust that the work you're doing today won't become tomorrow's burden.&lt;/p&gt;
&lt;p&gt;Everything we've discussed in this series—observability, resilience, adaptability, and thoughtful design—ultimately serves that purpose.&lt;/p&gt;
&lt;p&gt;Not to build perfect software, but to build confidence.&lt;/p&gt;
&lt;p&gt;Because when engineers trust the systems they're building and the people they're building them with, they make better decisions. Better decisions create better architecture. Better architecture creates momentum.&lt;/p&gt;
&lt;p&gt;And momentum is what allows great engineering organizations to keep building long after the first release.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Thank you for following along through this series.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My hope isn't that these articles convince anyone there's only one way to build software. Architecture has always been about tradeoffs, context, and the people making the decisions.&lt;/p&gt;
&lt;p&gt;If this series has a single message, it's this:&lt;/p&gt;
&lt;p&gt;Technology changes. Teams change. Businesses change.&lt;/p&gt;
&lt;p&gt;Trust is what allows all of them to move forward together.&lt;/p&gt;
&lt;p&gt;That's the kind of engineering culture I believe in.&lt;/p&gt;
&lt;p&gt;And that's the kind of company I'm building at M²S² Engineering Group.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25885/good-architecture-starts-with-trust</guid>
<pubDate>Mon, 31 Aug 2026 02:56:28 +0000</pubDate>
</item>
<item>
<title>Why I'm Building an Engineering Publishing Platform</title>
<link>https://coderlegion.com/25780/why-im-building-an-engineering-publishing-platform</link>
<description>&lt;p&gt;Over the past several months, I've made a commitment to write more.&lt;/p&gt;
&lt;p&gt;Not because I want to become a content creator, but because I've come to appreciate how much engineering knowledge quietly disappears every day. Architecture discussions end when the meeting is over. Production incidents become stories that fade with time. Lessons learned on one project rarely make it to the next. As engineers, we spend years building experience, but very little of it ever becomes knowledge that others can benefit from.&lt;/p&gt;
&lt;p&gt;Writing has become my way of changing that.&lt;/p&gt;
&lt;p&gt;Like many engineers, I started publishing wherever I could. I have a blog on my own website, along with accounts on Medium, Hashnode, DEV.to, and Coder Legion. Each platform reaches a different audience, and I genuinely like the idea of meeting engineers where they already are. If someone prefers reading on Hashnode instead of my website, that's perfectly fine with me. My goal has never been to drive traffic to a particular platform. It's to share ideas.&lt;/p&gt;
&lt;p&gt;Unfortunately, that turned out to be much harder than I expected.&lt;/p&gt;
&lt;p&gt;Every article followed the same routine. Once the writing was finished, I still had to copy Markdown between editors, upload images multiple times, recreate tags, tweak formatting, update SEO metadata, generate excerpts, and create social posts. By the time I finished publishing everywhere, I had spent almost as much time managing content as I had writing it.&lt;/p&gt;
&lt;p&gt;As an engineer, my first instinct was simple.&lt;/p&gt;
&lt;p&gt;Automate it.&lt;/p&gt;
&lt;p&gt;I envisioned a small service that would let me publish an article once and have it automatically appear everywhere else. My website would remain the source of truth while Medium, Hashnode, DEV.to, and every future platform would simply become destinations. It felt like a straightforward integration problem—build a few connectors, push the content out, and move on to the next article.&lt;/p&gt;
&lt;p&gt;Then I started designing it.&lt;/p&gt;
&lt;p&gt;Like many software projects, it didn't take long before I realized I was solving the wrong problem.&lt;/p&gt;
&lt;p&gt;The more I thought about the architecture, the less it resembled a collection of publishing scripts. If every platform was going to behave differently, I needed a common deployment model instead of a collection of one-off integrations. Articles would become immutable releases. Releases could be deployed to one or more publishing targets. Each destination would have its own connector responsible for understanding that platform, while the orchestration layer would manage deployments, retries, status, and lifecycle. The result was an architecture that felt much more like a software delivery pipeline than a blogging tool. &lt;/p&gt;
&lt;p&gt;That solved the technical problem.&lt;/p&gt;
&lt;p&gt;It didn't solve the real one.&lt;/p&gt;
&lt;p&gt;Somewhere in the middle of working through the design, I realized that publishing was never the difficult part.&lt;/p&gt;
&lt;p&gt;Thinking is.&lt;/p&gt;
&lt;p&gt;Every article I've written over the past few months has started long before a Markdown file ever existed. Sometimes it begins with a conversation. Sometimes it's a lesson I didn't fully appreciate until years later. Other times it's an architectural decision that seemed obvious in hindsight but required hours of discussion to reach. The words themselves are usually the final step in a much longer process of reflection.&lt;/p&gt;
&lt;p&gt;That's the process I don't want to lose.&lt;/p&gt;
&lt;p&gt;Most writing tools begin with a blank editor. They assume you already know exactly what you want to say. My experience has been almost the opposite. The most valuable part of writing is discovering the idea. It's connecting experiences that didn't seem related, questioning assumptions, and slowly uncovering the principle hidden underneath the story. By the time I begin writing, the real work has already happened.&lt;/p&gt;
&lt;p&gt;That realization changed the project entirely.&lt;/p&gt;
&lt;p&gt;I'm still building the cross-posting platform because I genuinely want to publish once and distribute everywhere with as little manual effort as possible. But that's no longer the vision.&lt;/p&gt;
&lt;p&gt;The vision is to build a platform that supports the entire journey from idea to publication. Conversations become drafts. Drafts become articles. Articles become releases that can be published anywhere. The platform should help organize ideas, generate summaries and metadata, manage revisions, and eventually understand the relationships between everything I've written. I don't want another tool that stores Markdown files. I want a system that helps preserve engineering judgment.&lt;/p&gt;
&lt;p&gt;Over the coming weeks, I'll be documenting the architecture, implementation, tradeoffs, and inevitable mistakes as I build this into the M²S² platform. Some articles will be technical deep dives into the connector architecture or deployment pipeline. Others will focus on the decisions that shaped the system and the lessons learned along the way. My hope is that readers don't just see the finished product—they understand the thinking behind it.&lt;/p&gt;
&lt;p&gt;If there's one thing I've learned throughout my career, it's that good engineering rarely starts with the perfect solution. More often, it starts with solving one problem, discovering a better question, and allowing the architecture to evolve alongside your understanding.&lt;/p&gt;
&lt;p&gt;That's exactly what happened here.&lt;/p&gt;
&lt;p&gt;I thought I was building a way to publish blog posts.&lt;/p&gt;
&lt;p&gt;Instead, I found myself designing a platform for capturing and sharing engineering experience.&lt;/p&gt;
&lt;p&gt;And I think that's a much more interesting problem to solve.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25780/why-im-building-an-engineering-publishing-platform</guid>
<pubDate>Sun, 30 Aug 2026 02:46:00 +0000</pubDate>
</item>
<item>
<title>Adding Edges Is Not a Paradigm Shift</title>
<link>https://coderlegion.com/24800/adding-edges-is-not-a-paradigm-shift</link>
<description>&lt;p&gt;I read the post, agreed with it, and opened my repo to plan the migration.&lt;/p&gt;
&lt;p&gt;Then I could not find the loop I was supposed to be replacing.&lt;/p&gt;
&lt;p&gt;The repo is agentsmyth, a seven-step agent chain I have been building for months. What I found was a fixed sequence with a checkpoint between every step and exactly one edge leaving every node. There was no loop to tear out and no obvious place to put a graph. I spent an evening trying to work out what I had misunderstood, and eventually went back to read the original post properly.&lt;/p&gt;
&lt;p&gt;It was a joke.&lt;/p&gt;
&lt;h3 id=&quot;the-joke-was-the-point&quot;&gt;The Joke Was the Point&lt;/h3&gt;
&lt;p&gt;Here is the sequence, because that sequence is the story.&lt;/p&gt;
&lt;p&gt;At 00:34 UTC on July 18, 2026, Peter Steinberger, the creator of OpenClaw, posted twelve words: &quot;Are we still talking loops or did we shift to graphs yet?&quot; No definition. No design principles. One line.&lt;/p&gt;
&lt;p&gt;About four and a half hours later, Hamel Husain published a piece declaring loop engineering dead. The next day the slogan was repeated, and a replacement narrative was moving before the replacement had a stable meaning. Within weeks there were guides, comparison tables, and a discipline with a name.&lt;/p&gt;
&lt;p&gt;Steinberger was not proposing a discipline. He was needling the field for how fast it renames things. Prompt engineering had become context engineering, which had become harness engineering, which had become loop engineering, and the underlying work had not changed nearly as much as the vocabulary had. He later posted a diagram of a two-state machine, looping and done, with a caption that left nothing to interpret:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;This is the silly thing you all hyped for weeks.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;The joke was the point, and the field wrote explainers about the punchline.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Harrison Chase, who created LangGraph, which is the system nearly every one of those explainers pointed to as the destination, responded to the wave by saying he did not know what graph engineering was and still did not, but that it was basically just LangGraph.&lt;/p&gt;
&lt;p&gt;Four days after that, he and Sydney Runkle published the piece that should have ended it. Three years of building graph agent systems, laid out plainly. Their position is not that graphs are overrated. It is that this was never a migration. Production agents need cycles, because they retry failed calls, ask for missing information, correct themselves after validation and pause for human input. A loop is a directed cyclic graph, a point they credit to David Khourshid. Loop engineering is not the alternative to graph engineering. It is the simple case of it.&lt;/p&gt;
&lt;p&gt;The LangChain framework itself runs on top of LangGraph, which means the migration a lot of people were planning had already happened underneath them, three years ago, without a name.&lt;/p&gt;
&lt;p&gt;The prior art runs deeper still. Anthropic documented prompt chaining, routing, parallelization, orchestrator-worker and evaluator-optimizer patterns back in 2024, with the recommendation to add complexity only when something demands it. LangGraph, AutoGen and Google ADK all shipped graph workflows before the word trended.&lt;/p&gt;
&lt;p&gt;So there was no paradigm to shift to. My chain is a graph. It always was one, with a single edge leaving every node.&lt;/p&gt;
&lt;p&gt;One more detail, and it is the one that stayed with me. The explainers cannot agree on how long the tweet was. One says six words. Another says nine. It is twelve. View counts range from 575,000 to 2.9 million depending on who is telling you. Dozens of articles were written about a single sentence, and a measurable share of them did not count the words in it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nobody checked the tweet.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That is the thing I actually want to be careful about. Not that the term is empty, because underneath it there is a real design problem about how multiple loops connect. But that a field can produce a month of confident writing on top of a line nobody read closely, and I was four paragraphs into planning a migration before I noticed I was one of them.&lt;/p&gt;
&lt;h3 id=&quot;an-edge-you-did-not-need-is-a-checkpoint-you-gave-away&quot;&gt;An Edge You Did Not Need Is a Checkpoint You Gave Away&lt;/h3&gt;
&lt;p&gt;That leaves the question worth asking, which is smaller and harder than the one I started with. Not whether to adopt graphs. What each additional edge is going to cost me.&lt;/p&gt;
&lt;p&gt;Here is the shape of what I built. A fixed sequence of seven steps, running from specification through planning, implementation, review, testing and release. One agent chain. A checkpoint between every step, where each step reads the written output of the one before it rather than the conversation. One human reviewer, which is me, standing at every checkpoint.&lt;/p&gt;
&lt;p&gt;Now put a bad spec into it. Not a broken one. A spec that is coherent, confident, and pointed slightly wrong, which is the only kind that survives long enough to hurt.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://raw.githubusercontent.com/JeelVankhede/JeelVankhede.github.io/main/adding-edges-is-not-a-paradigm-shift-fig-1.png&quot;&gt;&lt;img src=&quot;https://cdn.hashnode.com/uploads/covers/6a190ded78258754835f0cf3/a2ba0f68-3578-4428-899f-c3fd9ba9ed37.png&quot; alt=&quot;Two flows compared. The chain is the real seven-step sequence, with a bad spec entering at the first step and a checkpoint immediately after it, costing one step if caught. The graph splits the same spec across three nodes with no checkpoint between split and convergence, so the error only surfaces at the end.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the chain, that spec meets a checkpoint before anything is built. If I catch it, the cost is one step's work. If I miss it, it moves exactly one step, and there is another checkpoint waiting.&lt;/p&gt;
&lt;p&gt;Add edges and the same spec splits three ways. Three nodes build on it in parallel, each one confidently, each one correct with respect to the thing it was handed. The error does not surface at a checkpoint, because the spec already passed the only checkpoint it was going to meet. It surfaces at convergence, when the branches come back and do not fit each other.&lt;/p&gt;
&lt;p&gt;By then the work exists. &lt;em&gt;All of it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There is an obvious objection here, and it is correct.&lt;/p&gt;
&lt;p&gt;Nothing about a graph forbids checkpoints. LangGraph ships interrupts for exactly this, and human-in-the-loop pausing is a documented feature rather than an afterthought. You can gate every node in a branching system if you decide to, and serious teams do.&lt;/p&gt;
&lt;p&gt;So the claim is narrower than my diagram makes it look. A chain has one place to stand. A graph has as many places as it has branches, and every one of them has to be specified, staffed and waited on. Gating is not free in either shape. It is just cheap enough in a chain that I had stopped noticing I was paying for it.&lt;/p&gt;
&lt;p&gt;What I keep finding in the posts recommending the move is edges being added and checkpoints not being. The topology gets the diagram. The gating gets left as an exercise.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Every branch that runs unattended is a checkpoint I chose not to stand at.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have hit this failure before, at chain scale, and it was mine. Last year I handed an agent a global event mechanism across a React Native app. I wrote the plan, reviewed it, approved it. The agent implemented the approved plan correctly, and the app broke in places the plan had quietly skipped: passive components sitting outside the event scope, and a duplicate event firing that I only found by adding console logs to trace the flow.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Three days of fixing.&lt;/em&gt; The result landed around seventy to seventy-five percent aligned with what I had actually asked for.&lt;/p&gt;
&lt;p&gt;That was one agent, one sequence, one plan. &lt;strong&gt;The failure was never capability.&lt;/strong&gt; The plan had a hole and nothing in the shape of the process was built to find it before the building started.&lt;/p&gt;
&lt;p&gt;The checkpoint was there. I was standing at it. It still went through.&lt;/p&gt;
&lt;p&gt;That is the part that decides this. A checkpoint I miss costs one step, and I found the hole in three days because there was only one path to trace back along. I do not need to imagine what the same missed spec costs with three branches running through it at once.&lt;/p&gt;
&lt;h3 id=&quot;the-condition-i-have-not-met&quot;&gt;The Condition I Have Not Met&lt;/h3&gt;
&lt;p&gt;There is a clean version of when to add the edges, and it comes from people inside the discipline rather than from anyone selling the word. Add them when the path has to branch. When work has to route between multiple agents. When the run has to survive a human sitting in the middle of it.&lt;/p&gt;
&lt;p&gt;Against my system, nothing branches and there is one agent chain. The third one I arguably meet already, since I stop for a human at every step. Call it &lt;strong&gt;one for three&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I should be plain about the limit of my standing here. I have not built a multi-agent graph yet. This is an argument about whether I should, not a report from the other side of the decision. The people who have built them know things I do not &lt;em&gt;(And I know that few of them would claim that &quot;Loop engineering is dead!&quot; because they know everything around it)&lt;/em&gt;, and the LangChain writeup is the best evidence of that: their own team built early deep research on a predefined graph, then moved it toward a looser agentic loop, because the work would not sit still inside fixed nodes. They migrated away from a graph, on their own product, and said so publicly.&lt;/p&gt;
&lt;p&gt;Structure you have not earned costs you. That holds in both directions.&lt;/p&gt;
&lt;p&gt;So I am not adding graph support to agentsmyth. Not because graphs are hype, and not because the people building them are wrong. Because an edge is a complexity decision like any other. It pays back only when the work genuinely cannot be sequenced, and I have not brought it work that qualifies.&lt;/p&gt;
&lt;p&gt;Processes like this do not usually die from being wrong. They die from friction.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Every edge is a place the thing can catch.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What would flip it is specific, and I want to name it so I am held to it. A task where two parts genuinely cannot be sequenced, where waiting on the first is real waste and not just impatience, and where I can specify both halves well enough that they still fit when they come back. That third clause is the hard one. It is also the one nobody is talking about, because it is not a topology problem. It is the same specification problem, made harder by being cut in two.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The edges were never the interesting part. &lt;strong&gt;The spec was.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The seven-step chain this article describes is&lt;/em&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://jeelvankhede.github.io/agentsmyth&quot;&gt;&lt;em&gt;agentsmyth&lt;/em&gt;&lt;/a&gt;&lt;em&gt;. Install with&lt;/em&gt; &lt;code&gt;npx agentsmyth init&lt;/code&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24800/adding-edges-is-not-a-paradigm-shift</guid>
<pubDate>Mon, 17 Aug 2026 19:16:15 +0000</pubDate>
</item>
<item>
<title>Do Not Rewrite the Legacy System. Contain It.</title>
<link>https://coderlegion.com/23845/do-not-rewrite-the-legacy-system-contain-it</link>
<description>&lt;p&gt;Understand the existing design, then isolate the change, design it cleanly, and implement it with TDD, ATDD and integration testing.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Legacy systems usually do not need to be rewritten entirely. More often, a few parts create most of the pain, while the rest still works well enough.&lt;/p&gt;
&lt;p&gt;I start with a coarse view of the whole system (C4 models). The goal is not to document everything in detail, but to understand the main components, dependencies, integrations, ownership boundaries and finally to document the problems with the as-is implementation.&lt;/p&gt;
&lt;p&gt;Then, together with stakeholders, I identify which parts are most critical to change and assign priorities. Only after that do I start working on a concrete part.&lt;/p&gt;
&lt;p&gt;For the selected part, I explore the current implementation, analyse its behavior and document the design that actually exists, then create a coarse to-be C4 models and a more detailed UML models. Then I define a boundary around the part and introduce adapters that isolate the new design from the remaining legacy code, design the replacement cleanly and implement it with TDD.&lt;/p&gt;
&lt;p&gt;The same process can then be repeated for the next priority.&lt;/p&gt;
&lt;p&gt;The result is not an instant clean architecture. It is a system where more and more parts become understandable, documented and safe to change, one boundary at a time.&lt;/p&gt;
&lt;p&gt;This is the kind of legacy modernization work I specialize in: recovering design, containing change, and improving the system one boundary at a time.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23845/do-not-rewrite-the-legacy-system-contain-it</guid>
<pubDate>Fri, 31 Jul 2026 08:48:59 +0000</pubDate>
</item>
<item>
<title>It's Been a While, Here's What I've Been Building</title>
<link>https://coderlegion.com/22971/its-been-a-while-heres-what-ive-been-building</link>
<description>&lt;p&gt;It's been a while since I last published anything here on CoderLegion.&lt;/p&gt;
&lt;p&gt;I didn't stop building, I just got so deep into projects that writing took a back seat. So I thought it was time to share what I've been working on over the past few months.&lt;/p&gt;
&lt;h3 id=&quot;-building-afia9javtu&quot;&gt;🚀 Building Afia9jaVTU&lt;/h3&gt;
&lt;p&gt;One of the biggest projects I've worked on recently is &lt;strong&gt;Afia9jaVTU&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The goal wasn't just to create another VTU platform. I wanted to build something that could serve as a reliable foundation for digital services like airtime purchases, data subscriptions, wallet management, virtual accounts, bill payments, and more.&lt;/p&gt;
&lt;p&gt;Working on it pushed me to think beyond writing APIs. I had to consider system architecture, payment reliability, background jobs, user experience, and how to build software that can scale.&lt;/p&gt;
&lt;p&gt;It reminded me that software engineering is just as much about solving business problems as it is about writing clean code.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-building-alatpay-for-laravel&quot;&gt;💳 Building ALATPay for Laravel&lt;/h3&gt;
&lt;p&gt;Another project that kept me busy was creating &lt;strong&gt;ALATPay for Laravel&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I noticed there wasn't a modern Laravel package that made integrating ALATPay straightforward for Laravel developers.&lt;/p&gt;
&lt;p&gt;So I built one.&lt;/p&gt;
&lt;p&gt;The package handles much of the repetitive work developers face when integrating payments, allowing them to focus on their applications instead of boilerplate integration code.&lt;/p&gt;
&lt;p&gt;Building an open-source package is a different experience from building client software.&lt;/p&gt;
&lt;p&gt;You have to think about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Developer experience&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Backward compatibility&lt;/li&gt;
&lt;li&gt;Clean APIs&lt;/li&gt;
&lt;li&gt;Long term maintenance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's a challenge I genuinely enjoyed.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-solving-cgnat-problems-with-cgnat-inspector&quot;&gt;🌐 Solving CGNAT Problems with CGNAT Inspector&lt;/h3&gt;
&lt;p&gt;If you've ever hosted services from home, you've probably encountered CGNAT.&lt;/p&gt;
&lt;p&gt;It can be frustrating trying to figure out why port forwarding simply refuses to work.&lt;/p&gt;
&lt;p&gt;That's why I built &lt;strong&gt;CGNAT Inspector&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The goal is simple:&lt;/p&gt;
&lt;p&gt;Help developers and homelab enthusiasts quickly determine whether they're behind Carrier Grade NAT and understand what that means for self-hosting.&lt;/p&gt;
&lt;p&gt;Sometimes the best projects come from solving your own problems.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-experimenting-with-developer-tools&quot;&gt;🛠️ Experimenting with Developer Tools&lt;/h3&gt;
&lt;p&gt;Lately I've also been experimenting with building tools specifically for developers.&lt;/p&gt;
&lt;p&gt;Small utilities.&lt;/p&gt;
&lt;p&gt;Automation.&lt;/p&gt;
&lt;p&gt;CLI ideas.&lt;/p&gt;
&lt;p&gt;Infrastructure helpers.&lt;/p&gt;
&lt;p&gt;Things that remove friction from everyday development.&lt;/p&gt;
&lt;p&gt;Not every experiment becomes a finished product, but every experiment teaches something valuable.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-growing-royal-bcode-ventures&quot;&gt;🏢 Growing Royal Bcode Ventures&lt;/h3&gt;
&lt;p&gt;Outside open source, I've also been investing a lot of time into growing &lt;strong&gt;Royal Bcode Ventures (RBV)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Running a software company is a completely different challenge from writing software.&lt;/p&gt;
&lt;p&gt;You're thinking about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Finding clients&lt;/li&gt;
&lt;li&gt;Delivering projects&lt;/li&gt;
&lt;li&gt;Infrastructure&lt;/li&gt;
&lt;li&gt;Operations&lt;/li&gt;
&lt;li&gt;Customer support&lt;/li&gt;
&lt;li&gt;Marketing&lt;/li&gt;
&lt;li&gt;Finance&lt;/li&gt;
&lt;li&gt;Team growth&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It has taught me that building a business requires a very different mindset from building applications.&lt;/p&gt;
&lt;p&gt;Both are difficult but still rewarding.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-working-on-smartalert-t-sat-&quot;&gt;📡 Working on SmartAlert-T (SAT)&lt;/h3&gt;
&lt;p&gt;Another exciting project is &lt;strong&gt;SmartAlert-T (SAT)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The vision is to build a platform focused on intelligent monitoring and alerting.&lt;/p&gt;
&lt;p&gt;Whether it's servers, infrastructure, or critical systems, timely notifications can make all the difference.&lt;/p&gt;
&lt;p&gt;It's still evolving, but I'm excited about where it's headed.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-i-ve-learned&quot;&gt;📚 What I've Learned&lt;/h3&gt;
&lt;p&gt;Looking back, one thing stands out.&lt;/p&gt;
&lt;p&gt;The best way to improve as a developer isn't by following every new trend.&lt;/p&gt;
&lt;p&gt;It's by building.&lt;/p&gt;
&lt;p&gt;Real products.&lt;/p&gt;
&lt;p&gt;Real tools.&lt;/p&gt;
&lt;p&gt;Real businesses.&lt;/p&gt;
&lt;p&gt;Every project exposes new problems, and solving those problems teaches lessons no tutorial can.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-s-next-&quot;&gt;What's Next?&lt;/h3&gt;
&lt;p&gt;I'm planning to become much more active here on CoderLegion again.&lt;/p&gt;
&lt;p&gt;I'll be sharing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Behind the scenes architecture decisions&lt;/li&gt;
&lt;li&gt;Laravel tips&lt;/li&gt;
&lt;li&gt;Open-source projects&lt;/li&gt;
&lt;li&gt;Infrastructure experiments&lt;/li&gt;
&lt;li&gt;Lessons from running a software business&lt;/li&gt;
&lt;li&gt;Things that failed (because those are often the most educational)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you've also been building something interesting, I'd love to hear about it in the comments.&lt;/p&gt;
&lt;p&gt;Let's keep building.&lt;img src=&quot;https://monitor.myvirtualserver.com.ng/telemetry/clnzoxcy10001vy2ohi4obbi0/cmrvxyvvm000b8czcgclrenj3.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22971/its-been-a-while-heres-what-ive-been-building</guid>
<pubDate>Sat, 18 Jul 2026 18:03:45 +0000</pubDate>
</item>
<item>
<title>Why Modern Engineering Teams Must Prioritize System Observability Over Raw Performance</title>
<link>https://coderlegion.com/22051/why-modern-engineering-teams-must-prioritize-system-observability-over-raw-performance</link>
<description>&lt;p&gt;Engineering teams often chase performance: faster APIs, lower latency, optimized queries, reduced CPU usage. But in 2026, performance alone is no longer the defining factor of a reliable system. Modern architectures — distributed, event‑driven, containerized, multi‑service — demand something far more fundamental: [system observability][1].&lt;/p&gt;
&lt;p&gt;Performance tells you how fast your system is.&lt;br&gt;
Observability tells you why it behaves the way it does.&lt;/p&gt;
&lt;p&gt;And for engineering teams, especially small ones, this difference determines whether a system scales or collapses under pressure.&lt;/p&gt;
&lt;p&gt;Performance Without Observability Is a Blindfold&lt;br&gt;
A fast system can still be fragile.&lt;br&gt;
A highly optimized service can still fail silently.&lt;br&gt;
A low‑latency API can still degrade unpredictably under load.&lt;/p&gt;
&lt;p&gt;When teams focus exclusively on performance metrics, they optimize symptoms instead of causes. They see spikes, drops, delays — but they don’t see the internal relationships that create them.&lt;/p&gt;
&lt;p&gt;Observability exposes:&lt;/p&gt;
&lt;p&gt;hidden dependencies&lt;/p&gt;
&lt;p&gt;cascading failures&lt;/p&gt;
&lt;p&gt;slow internal paths&lt;/p&gt;
&lt;p&gt;unexpected retry storms&lt;/p&gt;
&lt;p&gt;misconfigured backoff logic&lt;/p&gt;
&lt;p&gt;silent timeouts&lt;/p&gt;
&lt;p&gt;resource starvation&lt;/p&gt;
&lt;p&gt;cross‑service bottlenecks&lt;/p&gt;
&lt;p&gt;Without this visibility, engineers operate reactively. They fix what they see, not what actually breaks the system.&lt;/p&gt;
&lt;p&gt;The Three Pillars of Observability&lt;br&gt;
Modern observability is not just logs. It is a structured, multi‑layered system that allows engineers to understand why something happened.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Metrics — the heartbeat of the system&lt;br&gt;
Metrics show trends, anomalies, and thresholds.&lt;br&gt;
They reveal when something is off — but not why.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Key engineering metrics include:&lt;/p&gt;
&lt;p&gt;request latency distribution&lt;/p&gt;
&lt;p&gt;error rate spikes&lt;/p&gt;
&lt;p&gt;queue depth growth&lt;/p&gt;
&lt;p&gt;retry counts&lt;/p&gt;
&lt;p&gt;CPU throttling events&lt;/p&gt;
&lt;p&gt;memory pressure&lt;/p&gt;
&lt;p&gt;cache hit ratios&lt;/p&gt;
&lt;p&gt;Metrics are the first signal, not the full story.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Logs — the narrative&lt;br&gt;
Logs provide context.&lt;br&gt;
They explain the events behind the metrics.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But logs alone are insufficient.&lt;br&gt;
Without structure, correlation, and context, logs become noise.&lt;/p&gt;
&lt;p&gt;Modern teams rely on:&lt;/p&gt;
&lt;p&gt;structured JSON logs&lt;/p&gt;
&lt;p&gt;correlation IDs&lt;/p&gt;
&lt;p&gt;distributed trace IDs&lt;/p&gt;
&lt;p&gt;log sampling&lt;/p&gt;
&lt;p&gt;log enrichment with metadata&lt;/p&gt;
&lt;p&gt;Logs transform raw events into actionable insights.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Traces — the map of the system&lt;br&gt;
Distributed tracing is the most powerful observability tool of 2026.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Traces show:&lt;/p&gt;
&lt;p&gt;how a request travels through the system&lt;/p&gt;
&lt;p&gt;where it slows down&lt;/p&gt;
&lt;p&gt;where it fails&lt;/p&gt;
&lt;p&gt;where retries accumulate&lt;/p&gt;
&lt;p&gt;where bottlenecks form&lt;/p&gt;
&lt;p&gt;where unexpected dependencies appear&lt;/p&gt;
&lt;p&gt;Traces reveal the architecture as it actually behaves — not as it was designed.&lt;/p&gt;
&lt;p&gt;Why Observability Matters More for Small Teams&lt;br&gt;
Large companies have SRE departments, dedicated monitoring teams, and 24/7 on‑call rotations.&lt;br&gt;
Small engineering teams do not.&lt;/p&gt;
&lt;p&gt;For small teams, observability is not a luxury — it is a multiplier.&lt;/p&gt;
&lt;p&gt;It allows them to:&lt;/p&gt;
&lt;p&gt;detect issues before users notice&lt;/p&gt;
&lt;p&gt;reduce on‑call stress&lt;/p&gt;
&lt;p&gt;shorten incident resolution time&lt;/p&gt;
&lt;p&gt;prevent cascading failures&lt;/p&gt;
&lt;p&gt;scale without rewriting the system&lt;/p&gt;
&lt;p&gt;understand real‑world behavior instead of assumptions&lt;/p&gt;
&lt;p&gt;Observability replaces guesswork with clarity.&lt;/p&gt;
&lt;p&gt;Observability Enables Predictable Engineering&lt;br&gt;
Predictability is the most valuable trait of a modern system.&lt;/p&gt;
&lt;p&gt;A predictable system:&lt;/p&gt;
&lt;p&gt;fails gracefully&lt;/p&gt;
&lt;p&gt;recovers automatically&lt;/p&gt;
&lt;p&gt;scales linearly&lt;/p&gt;
&lt;p&gt;behaves consistently under load&lt;/p&gt;
&lt;p&gt;provides clear signals when something is wrong&lt;/p&gt;
&lt;p&gt;Observability is the foundation of this predictability.&lt;br&gt;
It transforms engineering from reactive firefighting into proactive system design.&lt;/p&gt;
&lt;p&gt;The Future: Observability as a Core Engineering Skill&lt;br&gt;
In 2026, observability is no longer a DevOps specialty.&lt;br&gt;
It is a core engineering skill.&lt;/p&gt;
&lt;p&gt;Teams that master observability:&lt;/p&gt;
&lt;p&gt;ship faster&lt;/p&gt;
&lt;p&gt;debug faster&lt;/p&gt;
&lt;p&gt;scale safer&lt;/p&gt;
&lt;p&gt;maintain stability&lt;/p&gt;
&lt;p&gt;reduce operational cost&lt;/p&gt;
&lt;p&gt;build systems that survive real‑world conditions&lt;/p&gt;
&lt;p&gt;Performance matters.&lt;br&gt;
But system observability determines whether performance is sustainable.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22051/why-modern-engineering-teams-must-prioritize-system-observability-over-raw-performance</guid>
<pubDate>Wed, 15 Jul 2026 14:45:00 +0000</pubDate>
</item>
<item>
<title>Why Local Webhook Testing Still Feels Broken</title>
<link>https://coderlegion.com/22712/why-local-webhook-testing-still-feels-broken</link>
<description>&lt;h4 id=&quot;building-webhook-integrations-shouldn-t-require-deploying-your-application-every-time-you-make-a-change-&quot;&gt;&lt;em&gt;Building webhook integrations shouldn't require deploying your application every time you make a change.&lt;/em&gt;&lt;/h4&gt;
&lt;p&gt;One of the first things you discover when working with webhooks is that your local development environment isn't reachable from the internet.&lt;/p&gt;
&lt;p&gt;Your application might be running perfectly on &lt;code&gt;localhost:3000&lt;/code&gt;, but webhook providers like Stripe, GitHub, Clerk, Shopify, or Discord can't send requests to your machine.&lt;/p&gt;
&lt;p&gt;So the usual workflow begins.&lt;/p&gt;
&lt;p&gt;Start a tunnel.&lt;/p&gt;
&lt;p&gt;Copy the public URL.&lt;/p&gt;
&lt;p&gt;Update the webhook endpoint in the provider dashboard.&lt;/p&gt;
&lt;p&gt;Restart the tunnel.&lt;/p&gt;
&lt;p&gt;Copy a new URL.&lt;/p&gt;
&lt;p&gt;Update the provider again.&lt;/p&gt;
&lt;p&gt;Repeat.&lt;/p&gt;
&lt;p&gt;It works.&lt;/p&gt;
&lt;p&gt;But after doing it dozens of times across different projects, it starts to feel like unnecessary friction.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;local-development-shouldn-t-slow-you-down&quot;&gt;Local Development Shouldn't Slow You Down&lt;/h3&gt;
&lt;p&gt;When you're building a webhook integration, the goal is to iterate quickly.&lt;/p&gt;
&lt;p&gt;Receive an event.&lt;/p&gt;
&lt;p&gt;Inspect the payload.&lt;/p&gt;
&lt;p&gt;Change some code.&lt;/p&gt;
&lt;p&gt;Try again.&lt;/p&gt;
&lt;p&gt;Instead, developers often find themselves spending just as much time managing tunnel URLs as writing application logic.&lt;/p&gt;
&lt;p&gt;A tunnel expires.&lt;/p&gt;
&lt;p&gt;The URL changes.&lt;/p&gt;
&lt;p&gt;Every webhook provider now needs to be updated.&lt;/p&gt;
&lt;p&gt;If you're integrating with multiple services at once, that overhead grows quickly.&lt;/p&gt;
&lt;p&gt;It's a small problem that becomes surprisingly repetitive.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;tunnels-are-more-than-just-public-urls&quot;&gt;Tunnels Are More Than Just Public URLs&lt;/h3&gt;
&lt;p&gt;Most people think of a tunnel as a way to expose localhost.&lt;/p&gt;
&lt;p&gt;That's true.&lt;/p&gt;
&lt;p&gt;But for webhook development, it's also the bridge between your local application and the outside world.&lt;/p&gt;
&lt;p&gt;It allows you to receive real production-like events without deploying your application every time you want to test a small change.&lt;/p&gt;
&lt;p&gt;That feedback loop matters.&lt;/p&gt;
&lt;p&gt;The faster you can receive, inspect, and replay events, the faster you can build reliable integrations.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-i-built-tunnels-into-hooktrace&quot;&gt;Why I Built Tunnels into Hooktrace&lt;/h3&gt;
&lt;p&gt;While building Hooktrace, I realized developers were already using one tool to expose localhost and another to inspect webhook deliveries.&lt;/p&gt;
&lt;p&gt;That felt fragmented.&lt;/p&gt;
&lt;p&gt;Hooktrace already understands webhook events.&lt;/p&gt;
&lt;p&gt;It already captures requests, payloads, headers, retries, and delivery timelines.&lt;/p&gt;
&lt;p&gt;Adding tunnels means developers can expose a local endpoint and immediately start inspecting events from the same platform.&lt;/p&gt;
&lt;p&gt;No context switching.&lt;/p&gt;
&lt;p&gt;No juggling multiple dashboards.&lt;/p&gt;
&lt;p&gt;Just one place to receive, inspect, debug, and replay webhook traffic.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;it-s-not-about-replacing-existing-tools&quot;&gt;It's Not About Replacing Existing Tools&lt;/h3&gt;
&lt;p&gt;There are already excellent tunneling tools available, and I've used many of them myself.&lt;/p&gt;
&lt;p&gt;The goal isn't to replace them.&lt;/p&gt;
&lt;p&gt;The goal is to make the webhook development experience feel more connected.&lt;/p&gt;
&lt;p&gt;Instead of thinking about tunnels, event inspection, retries, aggregation, destinations, and observability as separate tools, I think they should work together.&lt;/p&gt;
&lt;p&gt;That's the direction I'm taking with Hooktrace.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;building-hooktrace-in-public&quot;&gt;Building Hooktrace in Public&lt;/h3&gt;
&lt;p&gt;Every feature I build starts with a problem I've run into while building integrations or backend systems.&lt;/p&gt;
&lt;p&gt;Sometimes it's webhook observability.&lt;/p&gt;
&lt;p&gt;Sometimes it's event aggregation.&lt;/p&gt;
&lt;p&gt;Sometimes it's routing events to different destinations.&lt;/p&gt;
&lt;p&gt;This week, it was local webhook testing.&lt;/p&gt;
&lt;p&gt;I'm documenting that journey in public—not just the finished product, but the engineering decisions, trade-offs, and lessons learned along the way.&lt;/p&gt;
&lt;p&gt;If you're building event-driven systems or working with webhooks every day, I'd love to hear how you're solving these problems.&lt;/p&gt;
&lt;p&gt;Good developer tools are shaped by real developer feedback.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;follow-along&quot;&gt;Follow Along&lt;/h3&gt;
&lt;p&gt;I'm building &lt;strong&gt;Hooktrace&lt;/strong&gt; in public and sharing the engineering journey one feature at a time.&lt;/p&gt;
&lt;p&gt;🌐 &lt;strong&gt;Hooktrace:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://hooktrace.xyz&quot;&gt;https://hooktrace.xyz&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;💻 &lt;strong&gt;Portfolio:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codilad.dev&quot;&gt;https://www.codilad.dev&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;⭐ &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/hooktracehq/hooktrace&quot;&gt;https://github.com/hooktracehq/hooktrace&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;𝕏 &lt;strong&gt;Follow the build:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://x.com/yasirbuilds&quot;&gt;https://x.com/yasirbuilds&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22712/why-local-webhook-testing-still-feels-broken</guid>
<pubDate>Wed, 15 Jul 2026 05:29:04 +0000</pubDate>
</item>
<item>
<title>Good Architecture is Built for Change</title>
<link>https://coderlegion.com/22249/good-architecture-is-built-for-change</link>
<description>&lt;p&gt;Good architecture is not built around the assumption that everything will stay the same.&lt;/p&gt;
&lt;p&gt;It is built around the reality that useful software will eventually need to change.&lt;/p&gt;
&lt;p&gt;A team may start with a clear product direction, a clean workflow, and a system that fits the business well. Then the business learns something. A customer asks for a workflow that was not part of the original model. A market opportunity changes the roadmap. A technical decision that made sense early starts to feel tighter as the product grows.&lt;/p&gt;
&lt;p&gt;That is normal.&lt;/p&gt;
&lt;p&gt;The problem is not that change happens. The problem is when the system was built in a way that makes reasonable change harder than it needs to be.&lt;/p&gt;
&lt;p&gt;Good architecture is not about predicting every future requirement. That usually leads to unnecessary abstractions, premature service boundaries, and complexity the team has not earned yet.&lt;/p&gt;
&lt;p&gt;Good architecture is about making change less expensive when it arrives.&lt;/p&gt;
&lt;p&gt;In the earlier posts in this series, I wrote about how good architecture creates engineering momentum by making important decisions visible early. I wrote about observability because teams need to understand and reason about systems in production. I wrote about failure planning because systems need to contain, recover from, and practice failure.&lt;/p&gt;
&lt;p&gt;Change is the next piece.&lt;/p&gt;
&lt;p&gt;A system can be observable, resilient, and well-built for its current purpose, but still become difficult to evolve if change was never part of the design conversation.&lt;/p&gt;
&lt;p&gt;That is where architecture either protects momentum or slowly drains it.&lt;/p&gt;
&lt;h3 id=&quot;change-is-the-real-test-of-architecture&quot;&gt;Change Is the Real Test of Architecture&lt;/h3&gt;
&lt;p&gt;Most systems look cleanest when they are new.&lt;/p&gt;
&lt;p&gt;The first version reflects the team’s best understanding at the time. The business rules are still fresh. The workflows are still easy to explain. The people who made the early decisions are usually still close to the work.&lt;/p&gt;
&lt;p&gt;Then the system starts living in the real world.&lt;/p&gt;
&lt;p&gt;The product grows. Customers use it in ways the team did not fully expect. Leadership sees new opportunities. Support learns where the workflow is confusing. Engineering discovers which assumptions were solid and which ones were only good enough for the first release.&lt;/p&gt;
&lt;p&gt;That is when architecture gets tested.&lt;/p&gt;
&lt;p&gt;In a system built for change, the team can usually find a reasonable place for the new behavior. The work may still be meaningful, but it does not feel mysterious. The structure of the system gives the team enough context to understand where the change belongs and what it might affect.&lt;/p&gt;
&lt;p&gt;In a system built only for the first version, each new requirement starts pulling on unrelated parts of the codebase. A small change in one workflow creates risk somewhere else. Engineers become cautious because the system no longer explains itself clearly.&lt;/p&gt;
&lt;p&gt;That is often how architectural pain begins.&lt;/p&gt;
&lt;p&gt;Not with one dramatic failure, but with the steady increase in cost every time the business learns something new.&lt;/p&gt;
&lt;h3 id=&quot;future-proofing-is-the-wrong-goal&quot;&gt;Future-Proofing Is the Wrong Goal&lt;/h3&gt;
&lt;p&gt;I am cautious about the phrase future-proof.&lt;/p&gt;
&lt;p&gt;It sounds responsible, but it can lead teams toward the wrong kind of design. When teams try to account for every possible future, they often add complexity long before the system needs it. The code fills with abstractions. The architecture gains extension points no one uses. Services get split before the domain is well understood.&lt;/p&gt;
&lt;p&gt;That kind of design can look sophisticated, but it often makes the current system harder to understand.&lt;/p&gt;
&lt;p&gt;The better goal is not future-proofing.&lt;/p&gt;
&lt;p&gt;The better goal is change tolerance.&lt;/p&gt;
&lt;p&gt;A change-tolerant system does not pretend to know every future requirement. It simply avoids making today’s decisions so rigid that tomorrow’s reasonable changes become painful. It keeps the important concepts clear. It separates concerns where separation has value. It gives the team room to adapt without forcing every change through the most fragile part of the system.&lt;/p&gt;
&lt;p&gt;That is a more practical standard.&lt;/p&gt;
&lt;p&gt;Good architecture accepts that the future is uncertain, but it does not use uncertainty as an excuse for either overengineering or neglect. It aims for enough structure to support change without adding so much structure that the team can no longer move.&lt;/p&gt;
&lt;h3 id=&quot;coupling-is-where-change-gets-expensive&quot;&gt;Coupling Is Where Change Gets Expensive&lt;/h3&gt;
&lt;p&gt;Most change pain comes from coupling.&lt;/p&gt;
&lt;p&gt;Some coupling is expected. Parts of a system need to work together. The issue is not that relationships exist. The issue is when those relationships become unclear, accidental, or too expensive to change.&lt;/p&gt;
&lt;p&gt;You can usually feel this in the work.&lt;/p&gt;
&lt;p&gt;A product request sounds simple, but the implementation touches several unrelated areas. A business rule exists in more than one place, and no one is completely sure which version is authoritative. A database table becomes the shared contract for workflows that should not know so much about each other. A user interface depends on backend details that were never meant to be part of the contract.&lt;/p&gt;
&lt;p&gt;At that point, the team is not only changing behavior. It is negotiating with hidden dependencies.&lt;/p&gt;
&lt;p&gt;That is one of the clearest signs architecture is working against the team.&lt;/p&gt;
&lt;p&gt;Poor architecture turns local changes into global events.&lt;/p&gt;
&lt;p&gt;Good architecture does the opposite. It keeps related things close and unrelated things separate enough that change can happen where it belongs.&lt;/p&gt;
&lt;h3 id=&quot;boundaries-should-reflect-the-business&quot;&gt;Boundaries Should Reflect the Business&lt;/h3&gt;
&lt;p&gt;One of the best ways to support change is to make sure the architecture reflects the business concepts the team is actually working with.&lt;/p&gt;
&lt;p&gt;This does not mean every business concept needs its own microservice. Distribution is not the same thing as good boundaries. In many cases, a modular monolith with clear internal structure is easier to change than a set of services split too early around concepts the team does not fully understand yet.&lt;/p&gt;
&lt;p&gt;The real question is whether responsibilities are clear.&lt;/p&gt;
&lt;p&gt;When boundaries reflect the business, changes become easier to discuss. Teams can talk about the system in the same language as the product. Ownership becomes more natural. New behavior has a clearer place to land.&lt;/p&gt;
&lt;p&gt;When boundaries are unclear, the system starts to resist change. Business rules spread across layers. Data ownership gets fuzzy. Teams spend more time deciding where work belongs than doing the work itself.&lt;/p&gt;
&lt;p&gt;Good boundaries do not eliminate hard decisions.&lt;/p&gt;
&lt;p&gt;They give teams a better starting point.&lt;/p&gt;
&lt;h3 id=&quot;good-architecture-makes-local-change-possible&quot;&gt;Good Architecture Makes Local Change Possible&lt;/h3&gt;
&lt;p&gt;A healthy architecture allows many changes to stay local.&lt;/p&gt;
&lt;p&gt;That does not mean every change is small. Some business changes are genuinely large and deserve careful planning. But when architecture is doing its job, a change to one concept does not automatically force the team to rework unrelated parts of the system.&lt;/p&gt;
&lt;p&gt;Local change matters because it preserves confidence.&lt;/p&gt;
&lt;p&gt;When engineers understand the boundary around the work, they can reason about impact. They know where to test. They can communicate risk more clearly. They can move without needing to hold the entire system in their head.&lt;/p&gt;
&lt;p&gt;That confidence compounds over time.&lt;/p&gt;
&lt;p&gt;A team that can make local changes builds trust in the system. A team that has to coordinate every change across too many areas starts to slow down. Eventually, the team spends more energy protecting itself from the system than improving the system.&lt;/p&gt;
&lt;p&gt;That is not just a tooling problem.&lt;/p&gt;
&lt;p&gt;It is a design problem.&lt;/p&gt;
&lt;p&gt;Good architecture gives change somewhere to go.&lt;/p&gt;
&lt;h3 id=&quot;contracts-help-systems-evolve&quot;&gt;Contracts Help Systems Evolve&lt;/h3&gt;
&lt;p&gt;As systems grow, contracts become increasingly important.&lt;/p&gt;
&lt;p&gt;An API contract, an event schema, a module boundary, or a data ownership rule is more than a technical detail. It is an agreement about how parts of the system interact.&lt;/p&gt;
&lt;p&gt;Good contracts make change safer because they define expectations. They allow one part of the system to evolve without surprising every other part. They create a place to discuss compatibility, versioning, migration, and ownership.&lt;/p&gt;
&lt;p&gt;Weak contracts create uncertainty.&lt;/p&gt;
&lt;p&gt;When the contract is unclear, consumers begin depending on behavior that was never intended. Internal details become external assumptions. Changing one side of the relationship becomes risky because no one is sure who depends on what.&lt;/p&gt;
&lt;p&gt;That is why compatibility matters.&lt;/p&gt;
&lt;p&gt;A system built for change needs a way to evolve without breaking everything at once. Sometimes that means versioned APIs. Sometimes it means backward-compatible event schemas. Sometimes it means migration windows, deprecation paths, or adapters that allow old and new behavior to coexist for a while.&lt;/p&gt;
&lt;p&gt;The specific technique depends on the system.&lt;/p&gt;
&lt;p&gt;The principle is the same: change becomes safer when expectations are explicit.&lt;/p&gt;
&lt;h3 id=&quot;change-requires-discipline-not-just-design&quot;&gt;Change Requires Discipline, Not Just Design&lt;/h3&gt;
&lt;p&gt;Architecture can make change easier, but it cannot carry the whole burden by itself.&lt;/p&gt;
&lt;p&gt;A system that is built for change still needs engineering discipline. Tests need to give the team confidence that behavior still works. Observability needs to show whether changes behave correctly in production. Documentation needs to capture the decisions that matter. Ownership needs to be clear enough that changes have a path through the team.&lt;/p&gt;
&lt;p&gt;Without those practices, even a well-designed system can degrade.&lt;/p&gt;
&lt;p&gt;Boundaries blur when teams ignore them. Contracts weaken when compatibility is not respected. Tests lose value when they stop reflecting real behavior. Documentation becomes useless when it stops matching reality.&lt;/p&gt;
&lt;p&gt;Good architecture creates the shape.&lt;/p&gt;
&lt;p&gt;Good engineering practice keeps that shape healthy.&lt;/p&gt;
&lt;p&gt;That is why adaptability is not a one-time design achievement. It is something a team maintains through repeated decisions.&lt;/p&gt;
&lt;h3 id=&quot;change-is-a-business-reality&quot;&gt;Change Is a Business Reality&lt;/h3&gt;
&lt;p&gt;Software exists to serve a business need, and business needs rarely stand still.&lt;/p&gt;
&lt;p&gt;That is why changeability has real business value.&lt;/p&gt;
&lt;p&gt;A system that can adapt helps the organization respond to new opportunities. It makes product learning less expensive. It helps teams move with more confidence when priorities shift. It reduces the fear that every new requirement will turn into a risky rewrite.&lt;/p&gt;
&lt;p&gt;A system that resists change creates a different kind of pressure.&lt;/p&gt;
&lt;p&gt;Roadmaps become harder to trust. Estimates become more uncertain. Teams hesitate to improve the system because every change feels connected to too many others. Eventually, the architecture begins shaping the business more than the business shapes the architecture.&lt;/p&gt;
&lt;p&gt;That is a dangerous place to be.&lt;/p&gt;
&lt;p&gt;Good architecture does not make change free.&lt;/p&gt;
&lt;p&gt;But it can keep change from becoming unnecessarily expensive.&lt;/p&gt;
&lt;h3 id=&quot;from-change-to-momentum&quot;&gt;From Change to Momentum&lt;/h3&gt;
&lt;p&gt;Engineering momentum depends on a team’s ability to keep moving as reality changes.&lt;/p&gt;
&lt;p&gt;That means understanding the system in production. It means planning for failure. It means designing boundaries, contracts, and feedback loops that allow the system to evolve without constantly fighting itself.&lt;/p&gt;
&lt;p&gt;Good architecture is built for change because useful software is never truly finished.&lt;/p&gt;
&lt;p&gt;It grows with the business. It responds to what teams learn. It adapts as customers, markets, and requirements become clearer.&lt;/p&gt;
&lt;p&gt;The goal is not to predict the future perfectly.&lt;/p&gt;
&lt;p&gt;The goal is to avoid trapping the team inside decisions that no longer fit.&lt;/p&gt;
&lt;p&gt;MARC², the M²S² architecture review bot, can help start that conversation by surfacing areas where coupling, unclear boundaries, or change risk may need more attention. From there, M²S² can help your team reason through the tradeoffs and turn those insights into practical architecture decisions.&lt;/p&gt;
&lt;p&gt;MARC² can help you start the conversation. M²S² can help you turn it into action.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22249/good-architecture-is-built-for-change</guid>
<pubDate>Fri, 10 Jul 2026 20:02:49 +0000</pubDate>
</item>
<item>
<title>Good Architecture Plans for Failure</title>
<link>https://coderlegion.com/22248/good-architecture-plans-for-failure</link>
<description>&lt;p&gt;Good architecture does not assume everything will work.&lt;/p&gt;
&lt;p&gt;It assumes something will fail and makes sure the team has a reasonable way to contain it, understand it, and recover from it.&lt;/p&gt;
&lt;p&gt;That may sound pessimistic, but it is really practical engineering. Every production system eventually meets reality. Dependencies slow down. Networks misbehave. Deployments change behavior. Queues back up. Users retry actions. Data arrives in shapes no one modeled perfectly.&lt;/p&gt;
&lt;p&gt;Failure is not always a sign that the architecture is bad.&lt;/p&gt;
&lt;p&gt;Failure is part of the environment the architecture has to operate within.&lt;/p&gt;
&lt;p&gt;In the first article of this series, I wrote about how good architecture creates engineering momentum by making important decisions visible early. In the second, I wrote about observability because teams need to understand and reason about systems in production.&lt;/p&gt;
&lt;p&gt;Failure planning is the next step.&lt;/p&gt;
&lt;p&gt;Observability helps a team see what is happening. Failure planning gives the team options when something goes wrong.&lt;/p&gt;
&lt;p&gt;A system is not well-designed because nothing ever fails. A system is well-designed when expected failures are contained, visible, and recoverable enough that they do not create unnecessary chaos.&lt;/p&gt;
&lt;h3 id=&quot;the-happy-path-is-not-the-whole-design&quot;&gt;The Happy Path Is Not the Whole Design&lt;/h3&gt;
&lt;p&gt;Most systems start with the happy path.&lt;/p&gt;
&lt;p&gt;A user takes an action. The API receives a request. The service validates the input. The database saves the record. An event is published. A downstream process reacts. The workflow completes.&lt;/p&gt;
&lt;p&gt;That path matters. It is usually the reason the system exists in the first place.&lt;/p&gt;
&lt;p&gt;But production systems are shaped just as much by the paths that do not go cleanly.&lt;/p&gt;
&lt;p&gt;The database write succeeds, but the event publish fails. A third-party service times out after the user has already submitted the request. A message is processed twice. A worker crashes halfway through a job. A deployment changes behavior in one service but not another. A user refreshes, retries, or submits the same action again because the interface did not clearly tell them what happened.&lt;/p&gt;
&lt;p&gt;These are not strange edge cases. They are normal conditions in real systems.&lt;/p&gt;
&lt;p&gt;When the design only accounts for the successful flow, the team is forced to discover the failure flow later. Usually that happens under pressure, when production is already misbehaving and the cost of learning is higher.&lt;/p&gt;
&lt;p&gt;Good architecture does not require every failure path to be solved upfront. That would be unrealistic. But it should make the most important failure paths visible early enough for the team to decide how they want the system to behave.&lt;/p&gt;
&lt;p&gt;That is the difference between accidental behavior and intentional design.&lt;/p&gt;
&lt;h3 id=&quot;reliability-lives-in-the-details&quot;&gt;Reliability Lives in the Details&lt;/h3&gt;
&lt;p&gt;Some of the most important architecture decisions look small at first.&lt;/p&gt;
&lt;p&gt;Timeouts are a good example. A timeout is not just a configuration value. It defines how long one part of the system is allowed to wait on another before protecting itself. Without clear timeouts, a slow dependency can hold resources, block requests, and create pressure in places that were never the original source of the problem.&lt;/p&gt;
&lt;p&gt;Retries are similar. Retrying a request can make a system more resilient when the failure is temporary. It can also make an outage worse if every client retries aggressively at the same time. A retry can recover work, or it can duplicate work. It depends on how the system is designed.&lt;/p&gt;
&lt;p&gt;That is where idempotency matters.&lt;/p&gt;
&lt;p&gt;If an operation may be attempted more than once, the system needs to know whether repeating it is safe. This matters for APIs, payments, event processing, background jobs, notifications, provisioning workflows, and any system where partial success is possible.&lt;/p&gt;
&lt;p&gt;A retry without idempotency is not resilience. It is a bet that the same action will not hurt you twice.&lt;/p&gt;
&lt;p&gt;These decisions often get treated as implementation details, but they shape the behavior of the system. They determine whether a failure stays small or spreads. They determine whether recovery is clean or messy. They determine whether the team can confidently automate retry behavior or has to manually clean up after every partial failure.&lt;/p&gt;
&lt;p&gt;Good architecture takes these details seriously because reliability is rarely created by one big decision. It is usually created by many small decisions that work together.&lt;/p&gt;
&lt;h3 id=&quot;contain-the-blast-radius&quot;&gt;Contain the Blast Radius&lt;/h3&gt;
&lt;p&gt;Once something fails, the next question is how far the failure should spread.&lt;/p&gt;
&lt;p&gt;That is where blast radius matters.&lt;/p&gt;
&lt;p&gt;Not every part of a system has the same business importance. If a reporting workflow is delayed, that may be acceptable. If checkout, login, or a core customer workflow fails, the impact is much higher. Good architecture recognizes that difference and avoids letting non-critical failure take down critical behavior.&lt;/p&gt;
&lt;p&gt;This is where boundaries matter. Service boundaries, queue boundaries, rate limits, circuit breakers, fallback behavior, and feature flags are all ways to control how failure moves through a system. None of them are magic. Each adds its own complexity. But when used intentionally, they give the team more control over how the system behaves under pressure.&lt;/p&gt;
&lt;p&gt;A downstream service being unavailable should not automatically mean every upstream workflow has to fail. A slow dependency should not be allowed to consume every resource in the system. A bad release should not require the entire platform to be rolled back if the change can be isolated or disabled.&lt;/p&gt;
&lt;p&gt;The point is not to make every system bulletproof.&lt;/p&gt;
&lt;p&gt;The point is to decide which failures are acceptable, which failures must be contained, and which parts of the system need to degrade gracefully instead of collapsing completely.&lt;/p&gt;
&lt;p&gt;That is an architectural decision.&lt;/p&gt;
&lt;p&gt;It is also a business decision.&lt;/p&gt;
&lt;h3 id=&quot;recovery-is-part-of-the-design&quot;&gt;Recovery Is Part of the Design&lt;/h3&gt;
&lt;p&gt;Prevention gets a lot of attention, but recovery matters just as much.&lt;/p&gt;
&lt;p&gt;No matter how carefully a system is designed, something will eventually go wrong. A deployment will introduce an issue. A migration will behave differently than expected. A message will fail. A third-party service will return something unusual. A job will process only part of the data it was supposed to process.&lt;/p&gt;
&lt;p&gt;The question is not only whether the team can prevent every problem.&lt;/p&gt;
&lt;p&gt;The question is whether the team knows what to do next.&lt;/p&gt;
&lt;p&gt;A system that supports recovery gives the team options. Failed messages can be retried or moved to a dead-letter queue. Events can be replayed. Data migrations have a rollback or repair strategy. Deployments can be reverted safely. Runbooks explain what to check and who owns the response.&lt;/p&gt;
&lt;p&gt;These things do not need to be overbuilt, but they should not be ignored.&lt;/p&gt;
&lt;p&gt;A system becomes more trustworthy when the team knows how to recover it. Engineers move differently when they know a failed job can be replayed, a bad deploy can be rolled back, or a partial failure can be repaired without guessing through production data.&lt;/p&gt;
&lt;p&gt;Recovery creates confidence.&lt;/p&gt;
&lt;p&gt;Confidence creates momentum.&lt;/p&gt;
&lt;h3 id=&quot;preparation-is-different-than-planning&quot;&gt;Preparation Is Different Than Planning&lt;/h3&gt;
&lt;p&gt;Planning for failure is important, but planning is only half the work.&lt;/p&gt;
&lt;p&gt;The other half is preparation.&lt;/p&gt;
&lt;p&gt;A team can document a rollback process, but that does not mean everyone knows how to use it under pressure. A system can have a recovery path, but that does not mean the path has been tested recently. A runbook can describe what to do during an outage, but if no one has practiced the steps, the first real incident becomes the training session.&lt;/p&gt;
&lt;p&gt;That is not where teams want to learn.&lt;/p&gt;
&lt;p&gt;Good architecture gives teams options when something goes wrong. Good preparation makes sure the team knows how to use those options.&lt;/p&gt;
&lt;p&gt;This is where practices like chaos days, game days, failure drills, and incident simulations become valuable. The goal is not to randomly break things for entertainment. The goal is to safely test assumptions, expose weak spots, and build confidence before the stakes are higher.&lt;/p&gt;
&lt;p&gt;A team might simulate a dependency outage, force a queue to back up, test a rollback, replay failed messages, rotate a secret, restore from backup, or walk through what happens when a critical service becomes unavailable. These exercises do not need to be large or dramatic to be useful. Even a small controlled failure can teach the team a lot about the system.&lt;/p&gt;
&lt;p&gt;The value is often in what the team discovers.&lt;/p&gt;
&lt;p&gt;Maybe an alert fires too late. Maybe the runbook is missing a step. Maybe only one person knows how to perform the recovery. Maybe a fallback path exists in code but has never been used in production. Maybe the system technically recovers, but the customer experience is worse than expected.&lt;/p&gt;
&lt;p&gt;Those are useful discoveries.&lt;/p&gt;
&lt;p&gt;Finding them during a practice exercise is far better than finding them during a real incident.&lt;/p&gt;
&lt;p&gt;Preparing for failure also changes team behavior. Engineers become more familiar with operational tools. Leaders gain a clearer understanding of system risk. Teams learn where the architecture is resilient and where it is still fragile.&lt;/p&gt;
&lt;p&gt;That kind of practice builds confidence.&lt;/p&gt;
&lt;p&gt;Planning gives the team a theory of recovery. Preparation proves whether that theory holds up.&lt;/p&gt;
&lt;h3 id=&quot;failure-planning-depends-on-observability&quot;&gt;Failure Planning Depends on Observability&lt;/h3&gt;
&lt;p&gt;Failure planning, preparation, and observability are closely connected.&lt;/p&gt;
&lt;p&gt;Observability helps the team see failure. Planning gives the team recovery options. Preparation teaches the team how to use those options when the system is under pressure.&lt;/p&gt;
&lt;p&gt;One without the others is incomplete.&lt;/p&gt;
&lt;p&gt;If a system has strong failure handling but weak observability, the team may not know when something is degrading or why. If a system has strong observability but no recovery path, the team may know exactly what went wrong and still have no safe way to fix it. If the team has a recovery plan but has never practiced it, the plan may not survive first contact with a real incident.&lt;/p&gt;
&lt;p&gt;The best systems give teams visibility, options, and rehearsal. They show where the failure happened, how far the impact spread, and what recovery paths are available. They also give the team enough practice to move from detection to understanding to action without starting from scratch.&lt;/p&gt;
&lt;p&gt;This is why failure planning belongs in the architecture conversation, not just the incident response process.&lt;/p&gt;
&lt;p&gt;By the time a system is failing in production, the team is mostly working with the options the architecture already gave them and the preparation they already invested in.&lt;/p&gt;
&lt;p&gt;Good architecture gives them better options.&lt;/p&gt;
&lt;p&gt;Good preparation helps them use those options well.&lt;/p&gt;
&lt;h3 id=&quot;failure-design-is-a-business-decision&quot;&gt;Failure Design Is a Business Decision&lt;/h3&gt;
&lt;p&gt;Not every system needs the same level of resilience.&lt;/p&gt;
&lt;p&gt;A prototype does not need the same failure planning as a payment system. An internal admin tool does not need the same guarantees as a customer-facing workflow. A reporting job that can run late does not need the same recovery model as a real-time user action.&lt;/p&gt;
&lt;p&gt;That is why designing for failure is not about making everything equally resilient. It is about matching the investment to the impact.&lt;/p&gt;
&lt;p&gt;Resilience has a cost. Redundancy, queues, retries, fallback paths, data replication, disaster recovery, and operational tooling all add complexity. Sometimes that complexity is justified. Sometimes it is not.&lt;/p&gt;
&lt;p&gt;Good architecture is honest about those tradeoffs.&lt;/p&gt;
&lt;p&gt;It considers what the business needs, what the team can operate, and what the system is responsible for protecting. It separates critical workflows from secondary ones. It identifies where failure must be avoided, where it can be tolerated, and where graceful degradation is good enough.&lt;/p&gt;
&lt;p&gt;The goal is not perfection.&lt;/p&gt;
&lt;p&gt;The goal is intentionality.&lt;/p&gt;
&lt;p&gt;When the team understands the consequences of failure, they can make better decisions about where to invest in reliability and where to keep the system simple.&lt;/p&gt;
&lt;h3 id=&quot;from-failure-paths-to-forward-momentum&quot;&gt;From Failure Paths to Forward Momentum&lt;/h3&gt;
&lt;p&gt;Good architecture does not eliminate failure. It gives teams a better way to handle it.&lt;/p&gt;
&lt;p&gt;That matters because engineering momentum is not just about how quickly a team can ship when everything is going well. It is also about how quickly the team can recover when something goes wrong.&lt;/p&gt;
&lt;p&gt;A team that understands its failure paths can move with more confidence. A team that has clear recovery options can release with less fear. A team that practices its response is less likely to discover basic operational gaps during a real incident.&lt;/p&gt;
&lt;p&gt;Failure planning is not about pessimism. It is about responsibility.&lt;/p&gt;
&lt;p&gt;It is about recognizing that production systems operate in imperfect environments and designing them so that failure does not automatically become chaos.&lt;/p&gt;
&lt;p&gt;That is why good architecture plans for failure.&lt;/p&gt;
&lt;p&gt;It helps teams contain the impact, understand what happened, recover with confidence, and keep moving.&lt;/p&gt;
&lt;p&gt;MARC², the M²S² architecture review bot, can help start that conversation by surfacing areas where failure behavior, reliability assumptions, or recovery paths may need more attention. From there, M²S² can help your team reason through the tradeoffs and turn those insights into practical architecture decisions.&lt;/p&gt;
&lt;p&gt;MARC² can help you start the conversation. M²S² can help you turn it into action.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22248/good-architecture-plans-for-failure</guid>
<pubDate>Fri, 10 Jul 2026 19:59:39 +0000</pubDate>
</item>
<item>
<title>When &quot;Latest&quot; isn't Actually Latest</title>
<link>https://coderlegion.com/21755/when-latest-isnt-actually-latest</link>
<description>&lt;h2 id=&quot;when-latest-isn-t-actually-latest&quot;&gt;When &quot;Latest&quot; Isn't Actually Latest&lt;/h2&gt;
&lt;h3 id=&quot;tl-dr&quot;&gt;TL;DR&lt;/h3&gt;
&lt;p&gt;In distributed systems, the last record your system processes is not always the most recent event that happened in reality.&lt;/p&gt;
&lt;p&gt;Understanding the difference between &lt;strong&gt;Event Time&lt;/strong&gt; and &lt;strong&gt;Processing Time&lt;/strong&gt; is essential to avoid data quality issues in streaming pipelines, CDC architectures, and event-driven systems.&lt;/p&gt;
&lt;h3 id=&quot;the-assumption-that-works-until-it-doesn-t&quot;&gt;The Assumption That Works... Until It Doesn't&lt;/h3&gt;
&lt;p&gt;Most developers start their careers working with a single application and a single database.&lt;/p&gt;
&lt;p&gt;In that world, time feels simple.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A customer updates their profile.&lt;/li&gt;
&lt;li&gt;The database stores the change.&lt;/li&gt;
&lt;li&gt;Everyone sees the updated record.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The sequence appears obvious because all operations happen within the same system.&lt;/p&gt;
&lt;p&gt;Then distributed systems enter the picture.&lt;/p&gt;
&lt;h3 id=&quot;a-simple-example&quot;&gt;A Simple Example&lt;/h3&gt;
&lt;p&gt;Imagine a customer updates their address at 10:00:00.&lt;/p&gt;
&lt;p&gt;That change generates an event and begins traveling through a distributed architecture:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Application&lt;/li&gt;
&lt;li&gt;Database&lt;/li&gt;
&lt;li&gt;CDC pipeline&lt;/li&gt;
&lt;li&gt;Kafka&lt;/li&gt;
&lt;li&gt;Consumer service&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Everything looks normal.&lt;/p&gt;
&lt;p&gt;Now imagine an older address update, generated earlier, gets delayed because of a network issue or retry mechanism.&lt;/p&gt;
&lt;p&gt;The result?&lt;/p&gt;
&lt;p&gt;The older event arrives after the newer one.&lt;/p&gt;
&lt;p&gt;Your consumer processes the events in this order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;New address&lt;/li&gt;
&lt;li&gt;Old address&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If your logic assumes that the last processed record represents the latest truth, you've just overwritten correct information with stale data.&lt;/p&gt;
&lt;h3 id=&quot;the-real-problem-time-has-multiple-meanings&quot;&gt;The Real Problem: Time Has Multiple Meanings&lt;/h3&gt;
&lt;p&gt;This is why distributed systems distinguish between two important concepts:&lt;/p&gt;
&lt;h4 id=&quot;event-time&quot;&gt;Event Time&lt;/h4&gt;
&lt;p&gt;The moment something actually happened in the real world.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Customer changed address → 10:00:00&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;processing-time&quot;&gt;Processing Time&lt;/h4&gt;
&lt;p&gt;The moment a system receives or processes the event.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consumer receives event → 10:00:15&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most of the time, these timestamps are close enough that nobody notices.&lt;/p&gt;
&lt;p&gt;The interesting cases appear when they aren't.&lt;/p&gt;
&lt;h3 id=&quot;where-things-get-complicated&quot;&gt;Where Things Get Complicated&lt;/h3&gt;
&lt;p&gt;Several factors can cause events to arrive out of order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Network latency&lt;/li&gt;
&lt;li&gt;Retries&lt;/li&gt;
&lt;li&gt;Consumer lag&lt;/li&gt;
&lt;li&gt;Parallel processing&lt;/li&gt;
&lt;li&gt;System failures&lt;/li&gt;
&lt;li&gt;Distributed transactions&lt;/li&gt;
&lt;li&gt;CDC replication delays&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As architectures become more distributed, these situations become normal rather than exceptional.&lt;/p&gt;
&lt;h3 id=&quot;what-about-kafka-&quot;&gt;What About Kafka?&lt;/h3&gt;
&lt;p&gt;Kafka helps preserve ordering within a partition through offsets.&lt;/p&gt;
&lt;p&gt;However, offsets tell us the order in which records were written to Kafka.&lt;/p&gt;
&lt;p&gt;They do not guarantee the order in which events actually occurred in the real world.&lt;/p&gt;
&lt;p&gt;That's an important distinction.&lt;/p&gt;
&lt;p&gt;A perfectly ordered Kafka partition can still contain events that represent out-of-order business actions.&lt;/p&gt;
&lt;h3 id=&quot;why-data-engineers-care&quot;&gt;Why Data Engineers Care&lt;/h3&gt;
&lt;p&gt;Many expensive data quality problems are not caused by broken infrastructure.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pipelines may be healthy.&lt;/li&gt;
&lt;li&gt;Kafka may be running perfectly.&lt;/li&gt;
&lt;li&gt;Consumers may be processing records successfully.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The issue is often more subtle.&lt;/p&gt;
&lt;p&gt;The system answers the right question using the wrong sequence of events.&lt;/p&gt;
&lt;p&gt;When that happens, dashboards become inaccurate, customer profiles become inconsistent, and downstream decisions become unreliable.&lt;/p&gt;
&lt;h3 id=&quot;final-thought&quot;&gt;Final Thought&lt;/h3&gt;
&lt;p&gt;Distributed systems force us to rethink a concept we usually take for granted: &lt;strong&gt;time&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The challenge isn't simply moving data from one system to another.&lt;/p&gt;
&lt;p&gt;The challenge is preserving the meaning of that data while it travels through a network of independent components.&lt;/p&gt;
&lt;p&gt;Because in distributed systems, the most recent record is not always the most recent event.&lt;/p&gt;
&lt;p&gt;And understanding that difference is often the first step toward building reliable data systems.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/21755/when-latest-isnt-actually-latest</guid>
<pubDate>Thu, 02 Jul 2026 11:08:55 +0000</pubDate>
</item>
<item>
<title>Good Architecture Includes Observability</title>
<link>https://coderlegion.com/20999/good-architecture-includes-observability</link>
<description>&lt;p&gt;Good architecture is not only about how a system is built. It is also about how well the team can understand that system once it is running.&lt;/p&gt;
&lt;p&gt;That is where observability belongs in the architecture conversation.&lt;/p&gt;
&lt;p&gt;It is common for observability to be treated as something that comes after the main engineering work. The service gets built. The API works. The deployment succeeds. Then, somewhere near the end, the team starts thinking about logs, dashboards, alerts, and operational visibility.&lt;/p&gt;
&lt;p&gt;That approach creates a gap.&lt;/p&gt;
&lt;p&gt;The architecture may look clean on paper, but once the system is in production, the team has to understand how it behaves under real conditions. Real users do not follow the happy path perfectly. Dependencies slow down. Queues back up. Data arrives in unexpected shapes. Deployments change behavior in ways that are not always obvious.&lt;/p&gt;
&lt;p&gt;If the system does not give the team a way to see those things clearly, the architecture is incomplete.&lt;/p&gt;
&lt;p&gt;Observability is not decoration around the system. It is part of the system design.&lt;/p&gt;
&lt;h3 id=&quot;architecture-describes-the-system-observability-shows-the-truth-&quot;&gt;Architecture Describes the System. Observability Shows the Truth.&lt;/h3&gt;
&lt;p&gt;Architecture is built on assumptions.&lt;/p&gt;
&lt;p&gt;During design, teams make reasonable guesses about usage patterns, service boundaries, dependency behavior, data flow, scale, latency, and failure modes. Some of those assumptions are based on experience. Some are based on current requirements. Some are simply the best call the team can make with the information available at the time.&lt;/p&gt;
&lt;p&gt;That is normal.&lt;/p&gt;
&lt;p&gt;The problem is not that architecture contains assumptions. Every architecture does.&lt;/p&gt;
&lt;p&gt;The problem is when those assumptions cannot be tested once the system is real.&lt;/p&gt;
&lt;p&gt;A design might assume that an external dependency will be reliable enough. Production may show that it is the slowest part of the request path. A queue might look like a clean decoupling point during design. Production may reveal retry behavior, duplication, or ordering concerns that were not obvious upfront. A serverless function might seem isolated and simple until timeouts, cold starts, and downstream limits begin shaping the user experience.&lt;/p&gt;
&lt;p&gt;Observability is how the team learns what reality looks like.&lt;/p&gt;
&lt;p&gt;Without it, production becomes a black box. The team may know something is wrong, but not where. They may see failures, but not the path that produced them. They may know customers are impacted, but not which customers, how often, or why.&lt;/p&gt;
&lt;p&gt;That is not just an operations problem.&lt;/p&gt;
&lt;p&gt;It is an architecture problem.&lt;/p&gt;
&lt;p&gt;If the team cannot understand how the system behaves in production, the design is missing a critical feedback loop.&lt;/p&gt;
&lt;h3 id=&quot;observability-is-more-than-having-logs&quot;&gt;Observability Is More Than Having Logs&lt;/h3&gt;
&lt;p&gt;A lot of systems have logs. Far fewer systems are actually observable.&lt;/p&gt;
&lt;p&gt;I have seen versions of the same story play out more than once. A team ships a feature, the deployment succeeds, and the basic dashboards look fine. Then support reports that a subset of users cannot complete a workflow. The application has logs, but the logs only show isolated fragments. One service logged the request. Another logged a timeout. A third recorded a retry. Somewhere downstream, a dependency returned an unexpected response.&lt;/p&gt;
&lt;p&gt;Nothing is completely invisible, but nothing is connected well enough to explain the full story.&lt;/p&gt;
&lt;p&gt;That is the pitfall.&lt;/p&gt;
&lt;p&gt;Teams do not need more disconnected data. They need enough context to move from symptom to cause.&lt;/p&gt;
&lt;p&gt;Good observability connects the signals that matter. It helps engineers follow the path of a request, understand where latency was introduced, see which dependency shaped the outcome, and determine whether the issue is isolated or systemic. It reduces the need to rely on memory, guesswork, or the one person who happens to know that part of the system best.&lt;/p&gt;
&lt;p&gt;Logs matter. Metrics matter. Traces, dashboards, and alerts matter too. But the toolset is not the point. The point is whether the team can ask useful questions about the system and get useful answers.&lt;/p&gt;
&lt;p&gt;Dumping logs into a platform does not automatically create understanding. Generic CPU and memory metrics may tell you something about infrastructure health without telling you much about user impact. Dashboards can look impressive while answering very little. Alerts can wake people up without giving them enough context to act.&lt;/p&gt;
&lt;p&gt;The goal is not more data.&lt;/p&gt;
&lt;p&gt;The goal is better understanding.&lt;/p&gt;
&lt;p&gt;That is why observability has to be designed intentionally.&lt;/p&gt;
&lt;h3 id=&quot;observability-changes-how-teams-work&quot;&gt;Observability Changes How Teams Work&lt;/h3&gt;
&lt;p&gt;The most obvious benefit of observability is faster debugging, but that is not the only benefit.&lt;/p&gt;
&lt;p&gt;The larger benefit is that observability changes the way teams make decisions.&lt;/p&gt;
&lt;p&gt;When a system is difficult to observe, conversations tend to become speculative. People rely on memory, intuition, and whatever signal is easiest to find. During incidents, that can create stress and confusion. After incidents, it can lead to shallow conclusions because the team never had a clear enough view of what actually happened.&lt;/p&gt;
&lt;p&gt;When a system is observable, the conversation changes.&lt;/p&gt;
&lt;p&gt;The team can look at the request path instead of debating which service might be slow. They can separate a deployment issue from a data issue. They can see whether a failure affected one customer, one workflow, or the entire system. They can understand whether a dependency is occasionally slow or consistently shaping the user experience.&lt;/p&gt;
&lt;p&gt;That kind of visibility creates better engineering behavior.&lt;/p&gt;
&lt;p&gt;Teams move from guessing to investigating. They move from opinion to evidence. They move from “it seems fine” to “here is what the system is telling us.”&lt;/p&gt;
&lt;p&gt;That feedback loop matters beyond incidents. It helps teams improve architecture over time. It shows where the system is under stress. It reveals dependencies that are more fragile than expected. It exposes workflows that are harder to support than they looked during design.&lt;/p&gt;
&lt;p&gt;Observability does not make systems simple, but it does make complexity easier to see.&lt;/p&gt;
&lt;p&gt;Visible complexity is much easier to manage than hidden complexity.&lt;/p&gt;
&lt;h3 id=&quot;observability-should-be-designed-early&quot;&gt;Observability Should Be Designed Early&lt;/h3&gt;
&lt;p&gt;Observability is much harder to add after the fact.&lt;/p&gt;
&lt;p&gt;Once a system is already built, teams often discover that the information they need was never captured. The logs are inconsistent. Trace context does not cross service boundaries. Metrics describe infrastructure health but not business impact. Alerts fire too late, too often, or without enough detail to act on.&lt;/p&gt;
&lt;p&gt;At that point, improving observability becomes another project layered on top of the original system.&lt;/p&gt;
&lt;p&gt;It is much easier to build visibility into the system while the architecture is still being shaped.&lt;/p&gt;
&lt;p&gt;That does not mean every project needs an elaborate observability platform on day one. It means the team should think about how the system will be understood once it is running. Critical workflows should leave a trail. Risky dependencies should be measurable. Failure modes should produce useful signals. The team should be able to tell whether the system is healthy from both a technical and user perspective.&lt;/p&gt;
&lt;p&gt;This becomes especially important as systems become more distributed.&lt;/p&gt;
&lt;p&gt;The more services, queues, functions, APIs, and third-party dependencies involved, the more important it becomes to preserve context across the flow. Without that context, every incident becomes a scavenger hunt across logs, dashboards, cloud consoles, and Slack messages.&lt;/p&gt;
&lt;p&gt;That scavenger hunt costs time, confidence, and momentum.&lt;/p&gt;
&lt;h3 id=&quot;the-best-signals-connect-to-real-impact&quot;&gt;The Best Signals Connect to Real Impact&lt;/h3&gt;
&lt;p&gt;Not every signal is equally useful.&lt;/p&gt;
&lt;p&gt;A team can collect hundreds of metrics and still struggle to understand whether users are having a good experience. A dashboard can show that infrastructure is healthy while a critical workflow is failing. An alert can be technically correct and still provide little value if it does not explain what is impacted or why anyone should care.&lt;/p&gt;
&lt;p&gt;Good observability connects technical behavior to real impact.&lt;/p&gt;
&lt;p&gt;Latency is not just a performance number; it represents a user waiting on the system. Error rates are not just percentages; they are workflows that did not complete. Queue depth is not just an operational metric; it is a sign that work is backing up somewhere in the system. Dependency failures matter because they change how gracefully the application can recover, degrade, or protect the user experience. Deployment events matter because every release changes the behavior of the system in some way, even when the deployment itself succeeds.&lt;/p&gt;
&lt;p&gt;The best signals help a team understand what is happening in terms that matter to both engineering and the business.&lt;/p&gt;
&lt;p&gt;This does not make infrastructure metrics unimportant. They are part of the picture. But they should not be the whole picture. Teams also need visibility into critical workflows, service boundaries, external dependencies, and the outcomes customers actually experience.&lt;/p&gt;
&lt;p&gt;A healthy architecture makes those relationships easier to see.&lt;/p&gt;
&lt;h3 id=&quot;observability-belongs-in-architecture-reviews&quot;&gt;Observability Belongs in Architecture Reviews&lt;/h3&gt;
&lt;p&gt;This is why observability belongs in architecture reviews.&lt;/p&gt;
&lt;p&gt;A design review should not stop at whether the components fit together. It should also consider how the system will be operated, supported, and improved once it is real.&lt;/p&gt;
&lt;p&gt;That conversation changes the quality of the architecture.&lt;/p&gt;
&lt;p&gt;It forces the team to think about where risk exists. It brings critical workflows into focus. It exposes dependencies that may need better monitoring or fallback behavior. It encourages teams to think about how they will diagnose failure before failure happens.&lt;/p&gt;
&lt;p&gt;This is also where &lt;strong&gt;MARC²&lt;/strong&gt;, the M²S² architecture review bot, can help start the conversation.&lt;/p&gt;
&lt;p&gt;A first-pass architecture review should surface more than service boundaries, data flow, and scaling assumptions. It should also help identify whether the system gives the team enough visibility to understand it in production.&lt;/p&gt;
&lt;p&gt;MARC² is not a replacement for experienced judgment, but it can help reveal where observability may be missing from the design conversation.&lt;/p&gt;
&lt;p&gt;That is a useful starting point.&lt;/p&gt;
&lt;h3 id=&quot;observability-is-part-of-engineering-momentum&quot;&gt;Observability Is Part of Engineering Momentum&lt;/h3&gt;
&lt;p&gt;Engineering momentum depends on a team’s ability to keep moving without constantly losing time to avoidable confusion.&lt;/p&gt;
&lt;p&gt;When observability is weak, teams lose time reconstructing context. Incidents take longer because the system does not clearly show where the problem started or how far the impact spread. Releases feel riskier because there is not enough confidence in how the system changed after deployment. Architecture decisions become harder to validate because the team has limited feedback from production.&lt;/p&gt;
&lt;p&gt;That uncertainty affects more than engineering. Leaders have less confidence in what is happening. Product conversations become harder to ground in reality. Teams spend more energy interpreting symptoms than improving the system.&lt;/p&gt;
&lt;p&gt;Strong observability creates a better feedback loop. It helps teams see how the system behaves, where pressure is building, and which decisions need to be revisited. It gives engineers and leaders a shared understanding of the system as it actually runs, not just as it was designed.&lt;/p&gt;
&lt;p&gt;That is why observability is not just an operational concern.&lt;/p&gt;
&lt;p&gt;It is part of good architecture.&lt;/p&gt;
&lt;p&gt;The code matters. The services matter. The deployment model matters. The data model matters. But the team’s ability to understand and reason about the system in production is the most important thing.&lt;/p&gt;
&lt;p&gt;A system that cannot be understood in production cannot be confidently operated.&lt;/p&gt;
&lt;p&gt;And a system that cannot be confidently operated will eventually slow the team down.&lt;/p&gt;
&lt;p&gt;Good architecture includes observability because good architecture is not only about building the system. It is about building a system the team can understand, operate, and improve.&lt;/p&gt;
&lt;p&gt;That is what keeps momentum alive.&lt;/p&gt;
&lt;h3 id=&quot;from-visibility-to-momentum&quot;&gt;From Visibility to Momentum&lt;/h3&gt;
&lt;p&gt;If your team is designing a new system, preparing for launch, or trying to understand why an existing platform has become harder to operate, observability is a good place to look.&lt;/p&gt;
&lt;p&gt;The question is not simply whether you have logs, metrics, traces, dashboards, or alerts. The better question is whether your team can understand what the system is doing when it matters most.&lt;/p&gt;
&lt;p&gt;That is the kind of visibility good architecture should provide.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MARC²&lt;/strong&gt;, the M²S² architecture review bot, can help start that conversation by surfacing architecture and observability gaps early. From there, M²S² can help your team reason through the tradeoffs, improve the design, and build the feedback loops needed to keep moving with confidence.&lt;/p&gt;
&lt;p&gt;MARC² can help you start the conversation. M²S² can help you turn it into action.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20999/good-architecture-includes-observability</guid>
<pubDate>Fri, 19 Jun 2026 23:04:28 +0000</pubDate>
</item>
<item>
<title>find . -delete Ran Before the Filter and Emptied the Whole Tree</title>
<link>https://coderlegion.com/20867/find-delete-ran-before-the-filter-and-emptied-the-whole-tree</link>
<description>&lt;p&gt;I meant to delete the &lt;code&gt;.cache&lt;/code&gt; files under a data directory. The server had been running for two months and the cache layer had grown to about 14GB. The application team told me it was safe to purge it — they'd rebuilt the cache logic and the old files were just dead weight. I typed &lt;code&gt;find /data -delete -name &quot;*.cache&quot;&lt;/code&gt; because I was moving fast and I figured the order of arguments to &lt;code&gt;find&lt;/code&gt; did not matter. It does. &lt;code&gt;find&lt;/code&gt; evaluates its expression left to right, and &lt;code&gt;-delete&lt;/code&gt; is not a filter — it is an action. It fired on every path &lt;code&gt;find&lt;/code&gt; walked, starting at &lt;code&gt;/data&lt;/code&gt; itself, and &lt;code&gt;-name &quot;*.cache&quot;&lt;/code&gt; never got a chance to narrow anything. By the time I hit Ctrl-C the tree was roughly 80% gone.&lt;/p&gt;
&lt;p&gt;That was not a test server.&lt;/p&gt;
&lt;p&gt;The restore from backup took forty minutes. The application was down for those forty minutes. The postmortem was a forty-five minute conversation with people who did not particularly enjoy having it. I have run hundreds of &lt;code&gt;find&lt;/code&gt; commands since then and I verify the expression order before every single one that has any destructive action attached to it — not because I've forgotten the rule, but because the cost of forgetting it once is not recoverable with an apology.&lt;/p&gt;
&lt;h3 id=&quot;why-the-order-is-the-program&quot;&gt;Why the order is the program&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;find&lt;/code&gt; does not have a flag parser that groups tests and actions separately. It walks a directory tree and evaluates its arguments as a logical expression, left to right, short-circuiting on false. When you write:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;find /data -name &quot;*.cache&quot; -delete
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;it evaluates &lt;code&gt;-name &quot;*.cache&quot;&lt;/code&gt; first on each path. If the name does not match, it short-circuits and &lt;code&gt;-delete&lt;/code&gt; never runs on that path. When you write:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;find /data -delete -name &quot;*.cache&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;it evaluates &lt;code&gt;-delete&lt;/code&gt; first. &lt;code&gt;-delete&lt;/code&gt; always succeeds — it removes the path and returns true, which means the expression continues to &lt;code&gt;-name&lt;/code&gt;. The name check runs after the deletion, on a file that no longer exists, which is meaningless. The effect is that everything gets deleted and nothing is filtered.&lt;/p&gt;
&lt;p&gt;This is not a bug. It is exactly how the man page says &lt;code&gt;find&lt;/code&gt; works. It is just not how anyone instinctively reads a command the first few times they use it.&lt;/p&gt;
&lt;p&gt;The rule is: &lt;strong&gt;tests filter, actions act, and actions must come after the tests that are supposed to narrow them.&lt;/strong&gt; Write it in that order every time.&lt;/p&gt;
&lt;h3 id=&quot;the-quoting-trap-right-behind-the-ordering-one&quot;&gt;The quoting trap, right behind the ordering one&lt;/h3&gt;
&lt;p&gt;Here is the one that is even more subtle, because it causes the wrong behavior and produces no error at all:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# WRONG — the shell expands *.cache before find ever sees it
find /data -name *.cache

# RIGHT — quote the pattern so find does the matching
find /data -name &quot;*.cache&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If there happens to be a single &lt;code&gt;.cache&lt;/code&gt; file in your current working directory when you run this, the shell expands &lt;code&gt;*.cache&lt;/code&gt; to that one filename and passes it to &lt;code&gt;find -name&lt;/code&gt; as a literal string. &lt;code&gt;find&lt;/code&gt; then searches for files named exactly that, everywhere under &lt;code&gt;/data&lt;/code&gt;. It finds some, it finds none, but it is definitely not doing a wildcard search. If there are multiple &lt;code&gt;.cache&lt;/code&gt; files in your current directory, &lt;code&gt;find&lt;/code&gt; receives too many arguments for &lt;code&gt;-name&lt;/code&gt; and errors out with something confusing about paths needing to precede the expression.&lt;/p&gt;
&lt;p&gt;Either way you did not get what you intended, and if you added &lt;code&gt;-delete&lt;/code&gt;, you deleted the wrong things silently.&lt;/p&gt;
&lt;p&gt;Quoting the pattern is the fix. The quotes prevent the shell from expanding the glob so &lt;code&gt;find&lt;/code&gt; receives the literal &lt;code&gt;*.cache&lt;/code&gt; pattern and handles the wildcard itself, across the directory tree you pointed it at.&lt;/p&gt;
&lt;h3 id=&quot;the-age-sign-that-everyone-reverses-at-least-once&quot;&gt;The age sign that everyone reverses at least once&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;find /var/log -name &quot;*.log&quot; -mtime +30   # older than 30 days
find /var/log -name &quot;*.log&quot; -mtime -1    # modified within the last day
find /var/log -name &quot;*.log&quot; -mtime 30    # exactly day 30 (almost never what you want)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;+30&lt;/code&gt; is older than thirty days. &lt;code&gt;-1&lt;/code&gt; is within the last day. A bare &lt;code&gt;30&lt;/code&gt; means precisely thirty days ago, which is almost never the thing you're trying to match. The sign convention is the opposite of what feels natural — you want files &quot;older than&quot; a threshold and the intuitive symbol for &quot;bigger number&quot; is &lt;code&gt;+&lt;/code&gt;, but a lot of people read &lt;code&gt;+30&lt;/code&gt; as &quot;in the last thirty days&quot; the first time they see it.&lt;/p&gt;
&lt;p&gt;I have reversed this twice in production. Once I kept the wrong logs. Once I deleted logs I needed for an audit the following week. Neither was catastrophic but both were embarrassing, and both happened under the kind of mild time pressure where double-checking the man page feels slower than it actually is. The builder I built for this labels the output as &quot;older than&quot; or &quot;within the last&quot; in plain English next to the value, which removes the one decision in a &lt;code&gt;find -delete&lt;/code&gt; job most likely to go wrong when you are already stressed.&lt;/p&gt;
&lt;h3 id=&quot;the--exec-batching-difference-nobody-explains&quot;&gt;The -exec batching difference nobody explains&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Runs the command once per file — slower, one PID per file
find /data -name &quot;*.cache&quot; -exec rm {} \;

# Batches files into one invocation — faster, one rm for many files
find /data -name &quot;*.cache&quot; -exec rm {} +
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;\;&lt;/code&gt; runs the command once per file. &lt;code&gt;+&lt;/code&gt; collects as many paths as it can and passes them all to one invocation of the command, the same way &lt;code&gt;xargs&lt;/code&gt; does. For something like &lt;code&gt;rm&lt;/code&gt;, which accepts multiple arguments, the &lt;code&gt;+&lt;/code&gt; form is faster and produces less process overhead. For something like a custom script that must process exactly one file at a time, &lt;code&gt;\;&lt;/code&gt; is correct.&lt;/p&gt;
&lt;p&gt;Most resources either do not mention this difference or mention it once in a reference table. The practical consequence is real — on a directory with ten thousand files, &lt;code&gt;\;&lt;/code&gt; spawns ten thousand processes. The &lt;code&gt;+&lt;/code&gt; form spawns a handful. On a cleanup job that runs in cron, the difference shows up in CPU load.&lt;/p&gt;
&lt;h3 id=&quot;the-preview-step-i-skipped-the-day-i-broke-things&quot;&gt;The preview step I skipped the day I broke things&lt;/h3&gt;
&lt;p&gt;The most reliable way to avoid the ordering and quoting mistakes is to build the command in two steps. First, run it with &lt;code&gt;-print&lt;/code&gt; instead of &lt;code&gt;-delete&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;find /data -name &quot;*.cache&quot; -print
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Read every line of that output. Confirm the list is what you intended. Then, and only then, swap &lt;code&gt;-print&lt;/code&gt; for &lt;code&gt;-delete&lt;/code&gt;. This adds maybe thirty seconds to the workflow. It would have saved me forty minutes and a postmortem. I skipped it because I was confident. Confidence is not a useful substitute for verification when the operation is irreversible.&lt;/p&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz/tools/find-command-builder&quot;&gt;find command builder&lt;/a&gt; enforces this by showing a warning whenever you select &lt;code&gt;-delete&lt;/code&gt; or &lt;code&gt;-exec&lt;/code&gt; as the action, and offering to generate the &lt;code&gt;-print&lt;/code&gt; version of the command first. It is the kind of nudge I would have appreciated having that day.&lt;/p&gt;
&lt;h3 id=&quot;what-the-builder-actually-does&quot;&gt;What the builder actually does&lt;/h3&gt;
&lt;p&gt;It assembles the expression in the legally correct order — tests first, action last — so you cannot accidentally replicate the mistake I made. You set the starting path, add tests in whatever order feels natural to you (name, type, age, size, exclude path), and pick an action. The output command always has the tests before the action, regardless of the order you clicked things in.&lt;/p&gt;
&lt;p&gt;Every active flag gets a plain-English description inline. &lt;code&gt;-mtime +30&lt;/code&gt; reads as &quot;modified more than 30 days ago.&quot; &lt;code&gt;-name &quot;*.cache&quot;&lt;/code&gt; reads as &quot;name matches the glob &lt;code&gt;*.cache&lt;/code&gt;, quoted so find handles the wildcard.&quot; The &lt;code&gt;-exec {} +&lt;/code&gt; form is the default when you pick &lt;code&gt;-exec&lt;/code&gt;, with a note explaining why it is faster.&lt;/p&gt;
&lt;p&gt;The whole point is to get from &quot;I need to find and delete files matching these conditions&quot; to a verified, copy-paste command without the thirty-second loop of man-page reading and second-guessing that I used to do and, on one bad morning, skipped.&lt;/p&gt;
&lt;h3 id=&quot;what-i-upgraded-while-i-was-at-it&quot;&gt;What I upgraded while I was at it&lt;/h3&gt;
&lt;p&gt;The same discipline applies to the other tools. The rsync command builder now has presets for the three setups people build most often — local backup, push to remote, mirror — because those cover maybe ninety percent of rsync jobs and getting the flags right from scratch every time is where the dangerous ones like &lt;code&gt;--delete&lt;/code&gt; get misapplied. The cron builder previews the next five run times after you build an expression, because a cron job I once deployed ran at 3am UTC instead of 3am local time and I did not notice until it fired on the wrong schedule for a week. You can also paste an existing crontab line and get the human-readable schedule back. The chmod builder now accepts an octal you paste in and sets the checkboxes — two-directional, because reading a file's permissions and understanding what they mean is just as common a task as setting them from scratch.&lt;/p&gt;
&lt;p&gt;These are not features I planned in advance. They are all things I needed at 2am and did not have. That is the pattern most of this site runs on.&lt;/p&gt;
&lt;p&gt;Build a &lt;code&gt;find&lt;/code&gt; command with tests ordered before actions and every flag explained: &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz/tools/find-command-builder&quot;&gt;https://bashsnippets.xyz/tools/find-command-builder&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you are scoping files before searching or transforming them, the whole pipeline is documented in &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz/guides/bash-text-processing&quot;&gt;Bash Text Processing: find, grep, sed, and awk&lt;/a&gt;. The rest of the free tools are at &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz/tools&quot;&gt;https://bashsnippets.xyz/tools&lt;/a&gt;&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/20867/find-delete-ran-before-the-filter-and-emptied-the-whole-tree</guid>
<pubDate>Wed, 17 Jun 2026 23:57:57 +0000</pubDate>
</item>
<item>
<title>Why Systems For Humans, Not People?</title>
<link>https://coderlegion.com/18775/why-systems-for-humans-not-people</link>
<description>&lt;p&gt;I run a newsletter called &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.mxomasuku.com/newsletter&quot;&gt;Systems For Humans&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I'm a software engineer. Not a data scientist, not an analyst, not a philosopher with credentials. But the longer I build software for businesses, the more I see software engineering and information systems collapse into one new practice. Call it behavioural engineering, or behaviour design, or whatever name eventually sticks. The naming is unsettled because the scale is unprecedented. But the practice itself is already running at planetary scale.&lt;/p&gt;
&lt;p&gt;So here goes my journey and insights so far.&lt;/p&gt;
&lt;p&gt;More people are connected now than at any point in human history. Behind that connectivity sits a cold, monstrous machine that sees people as data points. Zero sentiment.&lt;/p&gt;
&lt;p&gt;Every click. Every scroll. Every GPS coordinate your phone hands over. Every call. Every message you sent and every one you almost sent and deleted. You are a collection of data points to the machine, whether you like it or not.&lt;/p&gt;
&lt;p&gt;Back in the day, the business that won was the business that was good with people. Now the business that wins is the one that's good with human data points in relation to other data points. Thomas Davenport called this years ago. Every company eventually becomes a data company. By the time Google was running cloud engineering curricula around the idea, it had hardened into industry orthodoxy.&lt;/p&gt;
&lt;h3 id=&quot;human-vs-people&quot;&gt;Human vs people&lt;/h3&gt;
&lt;p&gt;So why call this Systems for Humans and not Systems for People?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A human is the term we use to describe a person as a scientific phenomenon.&lt;/strong&gt; Your emotions and sentiments aren't analysed for what they categorically are. They're analysed for what actions they drive you to take. Action economics drives the human world. Sentiment drives the people domain.&lt;/p&gt;
&lt;p&gt;Back in the day we were told that the business that won was the business that was good with people. That framing was a polite lie.&lt;/p&gt;
&lt;p&gt;Coca-Cola didn't win by just being &lt;em&gt;good&lt;/em&gt; with people.&lt;/p&gt;
&lt;p&gt;It was running one of the most successful behavioural engineering projects of the twentieth century, except it had to do it through television, billboards, and the radio. The craft was always deliberate. What we have today is a higher resolution, low latency, and broader reach version. ==The instruments got sharp enough to operate on you specifically, in real time, on a device you carry everywhere you go.== The practice didn't get invented. It got closer. It's now building an inescapable frame around you.&lt;/p&gt;
&lt;p&gt;When an organisation focuses on the human every problem becomes diagnosable. It becomes like a mechanic tuning a car. A surgeon operating on a sedated person. It can engineer systems and manipulate behaviour and anticipate actions. For the first time human experience is officially and truly a scientific economic problem in real observable time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why you do what you do as a person doesn't matter to it. What matters is that you did it. We had an interest in it. And we got a record of the event.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;why-this-matters-for-builders-in-zimbabwe&quot;&gt;Why this matters for builders in Zimbabwe&lt;/h3&gt;
&lt;p&gt;Look at why most tech startups here fail and why fintech succeeds.&lt;/p&gt;
&lt;p&gt;The average indie hacker thinks: &lt;em&gt;what do people want?&lt;/em&gt; He builds an app for that. The better question is: &lt;em&gt;what does the system need to be more efficient around this want?&lt;/em&gt; Because even if person A wants something, it won't go anywhere if it doesn't make the system more efficient. Fintech apps aren't about making people happy, they are just there to make the finance system more efficient and reliable.&lt;/p&gt;
&lt;p&gt;This creates a new problem for developers in places like ours. If you don't have access to data, you don't know what you're really building for. And the people who have the data know its value, so they won't give it away. Most of it is private anyway. In the end, ==we are locked out of the very inputs we'd need to build systems that actually serve the humans inside them.==&lt;/p&gt;
&lt;p&gt;The alternative is to build your own data bank. That's a whole new complexity most aren't equipped for, but ethics aside, it's the only honest path for builders in our position. Every fleet management app, every inventory tracker, every tool that gets deployed in Zimbabwe must become a data collection apparatus.&lt;/p&gt;
&lt;h3 id=&quot;let-s-talk-philosophy-for-a-bit&quot;&gt;Let's talk philosophy for a bit&lt;/h3&gt;
&lt;p&gt;You know the Ubuntu adage. &lt;em&gt;Umuntu ngumuntu ngabantu.&lt;/em&gt; I am because you are.&lt;/p&gt;
&lt;p&gt;For our grandparents, Ubuntu meant we must be empathetic. It confirmed that we build each other in community. From a data oriented point of view, Ubuntu means &lt;em&gt;you as a data point can influence me as a data point upon points of contact.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Ubuntu describes human existence relationally: a person becomes a person through contact with other people. Modern data systems describe humans relationally too, just differently. Social graphs, recommendation systems and behavioural models all reduce people into nodes, edges and probabilities. The philosophical language changed. The relational structure did not.&lt;/p&gt;
&lt;p&gt;In the early 20th century, after Europeans got tired of killing each other, a movement called humanism emerged. A philosophy that put human experience at the centre of society to prevent unnecessary suffering. It collected the known data points of suffering and produced a declaration recommending world leaders enhance human life accordingly.&lt;/p&gt;
&lt;p&gt;I'm being deliberate with that phrasing. Even the humanist project, the one that's supposed to protect us from being reduced to inputs, has always been a data project under the hood. Governments and institutions cannot respond to suffering one person at a time. Rather they aggregate patterns, statistics and outcomes. The difference is that humanism claims those abstractions should ultimately serve human flourishing rather than optimisation alone.&lt;/p&gt;
&lt;h3 id=&quot;where-this-is-going&quot;&gt;Where this is going&lt;/h3&gt;
&lt;p&gt;The future is data-driven. More complex and predictive algorithms will manipulate and drive human behaviour at scales we haven't seen yet. Self optimising profit-driven algorithms are the new overlords.&lt;/p&gt;
&lt;p&gt;My biggest fear is this: while humanist organisations fight governments to protect human rights, a new frontier for humanity is already being shaped. Systems for Humans means proactively building social and business systems that protect human interests on this frontier. And do not treat humanity as a consumable input or data point to be manipulated in service of the algorithm's grand design.&lt;/p&gt;
&lt;p&gt;The line between consumer modelling and political modelling is thinner than people realise. Rayid Ghani spent ten years at Accenture Technology Labs building algorithms that scored grocery shoppers on how savvy they were at hunting deals, how they assembled their baskets, what would move them to switch brands. In 2011 he left Accenture and became chief scientist for Obama's re-election campaign. The same modelling techniques he had used to predict whether a shopper would respond to a coupon were retooled to predict whether a voter would respond to a political appeal. The campaign's analytics operation is now widely described as having been potentially decisive in the 2012 election outcome. The grocery basket and the ballot turned out to be the same problem.&lt;/p&gt;
&lt;p&gt;Cathy O'Neil saw where this leads. In &lt;em&gt;Weapons of Math Destruction&lt;/em&gt; she argues that once you post something, or take an action that gets captured, it's no longer yours. It becomes the property of the company that controls the API that captured it. There is a version of you, a collection of every action you've ever taken, that is not yours. It belongs to them. And that version can be used in real time to manipulate your friends, your family, your daughters, anyone on the same platform.&lt;/p&gt;
&lt;p&gt;Including you.&lt;/p&gt;
&lt;p&gt;This isn't speculation. It's already the operating model.&lt;/p&gt;
&lt;p&gt;When OpenAI ships ChatGPT, they're not just engineering a model and serving it via API. They're shipping a system that reshapes how millions of people write, think, search, decide. The product is behavioural.&lt;/p&gt;
&lt;p&gt;When Meta optimises the feed, the engineering and the information architecture are both subordinate to a single goal: produce a specific behavioural outcome (more time on platform) by routing the right stimulus to the right person at the right moment.&lt;/p&gt;
&lt;h3 id=&quot;so-where-does-that-leave-you-&quot;&gt;So where does that leave you?&lt;/h3&gt;
&lt;p&gt;When you finally buy that upcoming PS6 you've been wanting, is it really you?&lt;/p&gt;
&lt;p&gt;Or is the purchase a data point the algorithm has been quietly developing for months? It starts as a reel: &lt;em&gt;gaming is life.&lt;/em&gt; You click. You watch. It records. Then comes another: &lt;em&gt;instead of alcohol, buy yourself leisure you won't regret.&lt;/em&gt; A steady stream of PS-vs-Xbox content ramps up. Until you buy.&lt;/p&gt;
&lt;p&gt;At which point in that sequence did your agency live? The very thing humanists insist sits at the centre of the philosophy, where exactly is it?&lt;/p&gt;
&lt;p&gt;If you want the practical companion to this, Mthokozisi Mabhena has written it. &lt;a rel=&quot;nofollow&quot; href=&quot;https://editingsociety.substack.com/p/poison-your-data&quot;&gt;Poison Your Data&lt;/a&gt; walks through Cambridge Analytica, Myanmar, the 2024 Taiwan AI persona networks, and lands on a concrete defence: data poisoning. Go read it. His piece is probably the data point that nudged me into writing this one.&lt;/p&gt;
&lt;p&gt;My question is the one upstream of his prescription. At what point does the humanist project still hold? &lt;strong&gt;When the systems are this good at predicting us, what's left of the agency humanism puts at the centre of everything?&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18775/why-systems-for-humans-not-people</guid>
<pubDate>Sat, 23 May 2026 19:11:40 +0000</pubDate>
</item>
<item>
<title>Hi Guys :) Help Me Choose the Best Payment Architecture for My Project</title>
<link>https://coderlegion.com/18340/hi-guys-help-me-choose-the-best-payment-architecture-for-my-project</link>
<description>&lt;p&gt;I’m currently building a mini project focused on a VIP car collection inventory platform designed for high-end clients who own and manage luxury or classic vehicle collections.&lt;/p&gt;
&lt;p&gt;One of the most important decisions right now is defining the payment architecture for the platform. Since the project is blockchain-oriented and built around Hyperledger Fabric concepts, I’m evaluating two different approaches:&lt;/p&gt;
&lt;p&gt;Option A — Native Token System (On-Chain)&lt;/p&gt;
&lt;p&gt;Create a native credit token inside the Fabric network.&lt;br&gt;
Clients deposit real money → tokens are issued to their wallet → platform services consume those tokens.&lt;br&gt;
This would create a fully on-chain ecosystem similar to ERC-20 logic, but implemented in Hyperledger Fabric.&lt;/p&gt;
&lt;p&gt;Option B — Off-Chain Payments + On-Chain Verification&lt;/p&gt;
&lt;p&gt;Payments happen externally through traditional methods (Stripe, bank transfer, etc.).&lt;br&gt;
The blockchain only stores a “payment confirmed” event along with a hash of the payment receipt for verification and auditability.&lt;/p&gt;
&lt;p&gt;What I’d Like Feedback On&lt;br&gt;
Which architecture would be more realistic for a production environment?&lt;br&gt;
Which option offers better scalability and maintainability?&lt;br&gt;
Which one would be more attractive for VIP/high-end clients?&lt;br&gt;
Would a fully on-chain payment experience actually add value in this type of application?&lt;br&gt;
From a software engineering perspective, which approach would you personally choose and why?&lt;/p&gt;
&lt;p&gt;I’d really appreciate opinions, technical feedback, architecture suggestions, or even hybrid alternatives. Thanks!&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/18340/hi-guys-help-me-choose-the-best-payment-architecture-for-my-project</guid>
<pubDate>Tue, 19 May 2026 16:26:42 +0000</pubDate>
</item>
<item>
<title>What I Learned Building a Multi-Agent AI System (That No Tutorial Warned Me About)</title>
<link>https://coderlegion.com/17221/what-i-learned-building-a-multi-agent-ai-system-that-no-tutorial-warned-me-about</link>
<description>&lt;p&gt;I’ve been building an AI platform called Wizard Ecosystem — a full-stack system with a multi-agent architecture, memory, tools, and orchestration layers.&lt;/p&gt;
&lt;p&gt;At first, I thought multi-agent systems would be simple:&lt;/p&gt;
&lt;p&gt;assign different roles to different agents&lt;br&gt;
chain them together&lt;br&gt;
let the orchestrator manage everything&lt;/p&gt;
&lt;p&gt;In reality, it became one of the most unpredictable systems I’ve worked on.&lt;/p&gt;
&lt;p&gt;This post is about the actual engineering problems I encountered while building it, and what I learned from them.&lt;/p&gt;
&lt;p&gt;The Architecture&lt;/p&gt;
&lt;p&gt;The system currently includes:&lt;/p&gt;
&lt;p&gt;multiple specialized agents (researcher, writer, reviewer, coder, optimizer, translator, etc.)&lt;br&gt;
orchestration logic for routing tasks&lt;br&gt;
persistent memory systems&lt;br&gt;
RAG/document retrieval&lt;br&gt;
web search integration&lt;br&gt;
tool execution&lt;br&gt;
a Python SDK/API layer&lt;br&gt;
multiple connected applications inside the ecosystem&lt;/p&gt;
&lt;p&gt;The backend is primarily built using:&lt;/p&gt;
&lt;p&gt;Python&lt;br&gt;
Flask&lt;br&gt;
Groq-hosted Llama models&lt;br&gt;
modular routing and orchestration systems&lt;/p&gt;
&lt;p&gt;What started as a chatbot slowly became a distributed AI workflow system.&lt;/p&gt;
&lt;p&gt;Problem 1: Agents Don’t Naturally Cooperate&lt;/p&gt;
&lt;p&gt;My first assumption was:&lt;/p&gt;
&lt;p&gt;“If every agent has a role, they’ll behave consistently.”&lt;/p&gt;
&lt;p&gt;That turned out to be completely wrong.&lt;/p&gt;
&lt;p&gt;What actually happened:&lt;/p&gt;
&lt;p&gt;reviewer agents contradicted writer agents&lt;br&gt;
coder agents ignored earlier constraints&lt;br&gt;
multiple agents repeated identical reasoning&lt;br&gt;
responses drifted between executions&lt;/p&gt;
&lt;p&gt;The biggest surprise was realizing:&lt;/p&gt;
&lt;p&gt;LLMs are not stable workers.&lt;/p&gt;
&lt;p&gt;They are probabilistic generators.&lt;/p&gt;
&lt;p&gt;Even with identical prompts, outputs can vary enough to destabilize workflows.&lt;/p&gt;
&lt;p&gt;Problem 2: Prompting Is Not Architecture&lt;/p&gt;
&lt;p&gt;Initially, I tried fixing everything with better prompts.&lt;/p&gt;
&lt;p&gt;That worked temporarily, but the system kept breaking in edge cases.&lt;/p&gt;
&lt;p&gt;Eventually, I realized:&lt;/p&gt;
&lt;p&gt;Prompts are configuration — not control.&lt;/p&gt;
&lt;p&gt;The actual stability came from:&lt;/p&gt;
&lt;p&gt;orchestration rules&lt;br&gt;
validation layers&lt;br&gt;
structured input/output schemas&lt;br&gt;
limiting unnecessary agent interactions&lt;/p&gt;
&lt;p&gt;Without external structure, multi-agent systems become chaotic very quickly.&lt;/p&gt;
&lt;p&gt;Problem 3: Orchestration Became the Hardest Component&lt;/p&gt;
&lt;p&gt;I originally thought the difficult part would be generation quality.&lt;/p&gt;
&lt;p&gt;Instead, the hardest problem became:&lt;/p&gt;
&lt;p&gt;deciding which agent should act, when, and with what context.&lt;/p&gt;
&lt;p&gt;Some issues I encountered:&lt;/p&gt;
&lt;p&gt;recursive reviewer loops&lt;br&gt;
duplicate task execution&lt;br&gt;
context overload&lt;br&gt;
conflicting outputs from individually “correct” agents&lt;/p&gt;
&lt;p&gt;This forced me to redesign the orchestrator multiple times.&lt;/p&gt;
&lt;p&gt;The more agents I added, the more important coordination became.&lt;/p&gt;
&lt;p&gt;Problem 4: Memory Is Dangerous&lt;/p&gt;
&lt;p&gt;Persistent memory sounded easy:&lt;/p&gt;
&lt;p&gt;store user information&lt;br&gt;
retrieve it later&lt;br&gt;
improve continuity&lt;/p&gt;
&lt;p&gt;But memory introduced entirely new failure modes:&lt;/p&gt;
&lt;p&gt;outdated context affecting future responses&lt;br&gt;
incorrect assumptions being reinforced&lt;br&gt;
irrelevant memory retrieval&lt;br&gt;
hallucinated continuity&lt;/p&gt;
&lt;p&gt;I learned that:&lt;/p&gt;
&lt;p&gt;Memory in AI systems behaves more like active bias injection than passive storage.&lt;/p&gt;
&lt;p&gt;It requires:&lt;/p&gt;
&lt;p&gt;filtering&lt;br&gt;
relevance scoring&lt;br&gt;
strict context management&lt;br&gt;
Problem 5: Latency Breaks the Illusion&lt;/p&gt;
&lt;p&gt;Even using fast inference through Groq-hosted models, latency became a real issue.&lt;/p&gt;
&lt;p&gt;Multi-agent systems amplify delays because:&lt;/p&gt;
&lt;p&gt;every agent call adds overhead&lt;br&gt;
orchestration introduces routing time&lt;br&gt;
chained execution increases unpredictability&lt;/p&gt;
&lt;p&gt;A technically “smart” system can still feel unintelligent if response timing becomes inconsistent.&lt;/p&gt;
&lt;p&gt;That was something I underestimated early on.&lt;/p&gt;
&lt;p&gt;The Biggest Insight&lt;/p&gt;
&lt;p&gt;After rebuilding parts of the system multiple times, I realized:&lt;/p&gt;
&lt;p&gt;The hardest part of multi-agent systems is not intelligence — it’s coordination.&lt;/p&gt;
&lt;p&gt;Most tutorials focus on:&lt;/p&gt;
&lt;p&gt;prompts&lt;br&gt;
models&lt;br&gt;
agents themselves&lt;/p&gt;
&lt;p&gt;But the real engineering challenge is:&lt;/p&gt;
&lt;p&gt;orchestration&lt;br&gt;
state management&lt;br&gt;
validation&lt;br&gt;
system predictability&lt;/p&gt;
&lt;p&gt;The intelligence of the system depends heavily on the structure surrounding the models.&lt;/p&gt;
&lt;p&gt;What I Changed&lt;/p&gt;
&lt;p&gt;To stabilize the system, I eventually:&lt;/p&gt;
&lt;p&gt;centralized orchestration logic&lt;br&gt;
standardized prompts through a prompt builder&lt;br&gt;
reduced unnecessary agent chaining&lt;br&gt;
added validation and reviewer stages&lt;br&gt;
simplified routing behavior&lt;br&gt;
limited memory retrieval scope&lt;/p&gt;
&lt;p&gt;Ironically, making the system more constrained improved overall quality significantly.&lt;/p&gt;
&lt;p&gt;Final Thoughts&lt;/p&gt;
&lt;p&gt;Building a multi-agent AI platform completely changed how I think about software systems.&lt;/p&gt;
&lt;p&gt;At first, I viewed LLMs as:&lt;/p&gt;
&lt;p&gt;“smart tools.”&lt;/p&gt;
&lt;p&gt;Now I view them more like:&lt;/p&gt;
&lt;p&gt;non-deterministic distributed system components.&lt;/p&gt;
&lt;p&gt;That changes everything about how you design around them.&lt;/p&gt;
&lt;p&gt;The biggest lesson I learned is:&lt;/p&gt;
&lt;p&gt;AI systems become far more reliable when architecture is prioritized over raw generation.&lt;/p&gt;
&lt;p&gt;There’s still a lot I’m improving, but building these systems has been one of the most interesting engineering experiences I’ve worked on so far.&lt;/p&gt;
&lt;p&gt;Originally published on Dev.to:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/ag_wizai&quot;&gt;https://dev.to/ag_wizai&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/17221/what-i-learned-building-a-multi-agent-ai-system-that-no-tutorial-warned-me-about</guid>
<pubDate>Sun, 10 May 2026 05:30:26 +0000</pubDate>
</item>
<item>
<title>Building Production-Ready Spring Boot Microservices: Lessons from Scaling to Millions</title>
<link>https://coderlegion.com/11476/building-production-ready-spring-boot-microservices-lessons-from-scaling-to-millions</link>
<description>&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;Over the past several years working in e-commerce, I've architected and scaled microservices handling millions of customer requests daily, including platforms processing over $100M in transactions. In this article, I'll share practical patterns for building Spring Boot microservices that can handle production scale.&lt;/p&gt;
&lt;p&gt;This isn't theory—these are battle-tested approaches proven under real-world load.&lt;/p&gt;
&lt;h3 id=&quot;1-service-boundaries-and-domain-driven-design&quot;&gt;1. Service Boundaries and Domain-Driven Design&lt;/h3&gt;
&lt;p&gt;The biggest mistake teams make is creating microservices that are too granular or tightly coupled.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key principle:&lt;/strong&gt; Each service should own a complete business capability.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// ❌ BAD: Services that are too granular
CustomerNameService
CustomerAddressService

// ✅ GOOD: Services that own complete domains
CustomerProfileService // owns all customer identity/profile data
SubscriptionService    // owns subscriptions, benefits, rewards
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When building a subscription platform, a &lt;code&gt;SubscriptionService&lt;/code&gt; should own the complete domain: enrollment, benefits tracking, payment integration, and conversions. This prevents the &quot;distributed monolith&quot; where you call 10 services for one operation.&lt;/p&gt;
&lt;h3 id=&quot;2-handling-distributed-transactions-with-saga-pattern&quot;&gt;2. Handling Distributed Transactions with Saga Pattern&lt;/h3&gt;
&lt;p&gt;At scale, distributed transactions become your enemy. Here's the orchestration approach:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Service
public class SubscriptionEnrollmentOrchestrator {
    
    @Transactional
    public EnrollmentResult enrollMember(EnrollmentRequest request) {
        // Step 1: Validate customer (synchronous)
        CustomerProfile customer = customerClient.getCustomer(request.getCustomerId())
            .orElseThrow(() -&amp;gt; new CustomerNotFoundException());
            
        // Step 2: Process payment (synchronous with compensation)
        PaymentResult payment;
        try {
            payment = paymentClient.processPayment(request.getPaymentMethod());
        } catch (PaymentException e) {
            throw new EnrollmentFailedException(&quot;Payment failed&quot;, e);
        }
        
        // Step 3: Create subscription (local transaction)
        Subscription subscription;
        try {
            subscription = createSubscription(customer, payment);
            subscriptionRepository.save(subscription);
        } catch (Exception e) {
            // Compensate payment
            paymentClient.refundPayment(payment.getTransactionId());
            throw new EnrollmentFailedException(&quot;Subscription creation failed&quot;, e);
        }
        
        // Step 4: Publish event for async operations
        eventPublisher.publish(new SubscriptionEnrolledEvent(subscription));
        
        return EnrollmentResult.success(subscription);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Key lessons:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Make operations idempotent&lt;/strong&gt; - Include transaction IDs so retries don't cause duplicates&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compensate explicitly&lt;/strong&gt; - If step N fails, undo steps 1 through N-1&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separate critical path from eventual consistency&lt;/strong&gt; - Payment is synchronous, emails are async&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;3-strategic-caching-for-high-traffic-systems&quot;&gt;3. Strategic Caching for High-Traffic Systems&lt;/h3&gt;
&lt;p&gt;We reduced database load by 85% through strategic caching:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Service
public class SubscriptionService {
    
    private final LoadingCache&amp;lt;String, Optional&amp;lt;Subscription&amp;gt;&amp;gt; cache;
    
    public SubscriptionService(SubscriptionRepository repository) {
        this.cache = CacheBuilder.newBuilder()
            .maximumSize(10_000)
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .recordStats()
            .build(CacheLoader.from(repository::findByCustomerId));
    }
    
    @Transactional
    public Subscription updateSubscription(String id, SubscriptionUpdate update) {
        Subscription updated = repository.findById(id)
            .map(s -&amp;gt; applyUpdate(s, update))
            .orElseThrow(() -&amp;gt; new SubscriptionNotFoundException(id));
            
        repository.save(updated);
        cache.invalidate(updated.getCustomerId()); // Invalidate on write
        
        return updated;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Caching layers:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Application-level (Guava/Caffeine) - 5-minute TTL for hot data&lt;/li&gt;
&lt;li&gt;Redis distributed cache - 15-minute TTL for cross-instance consistency&lt;/li&gt;
&lt;li&gt;Database with proper indexing&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;4-circuit-breakers-and-resilience&quot;&gt;4. Circuit Breakers and Resilience&lt;/h3&gt;
&lt;p&gt;When calling other services, assume they will fail:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Service
public class CustomerProfileClient {
    
    @CircuitBreaker(name = &quot;customer-profile&quot;, fallbackMethod = &quot;getCustomerFallback&quot;)
    @TimeLimiter(name = &quot;customer-profile&quot;)
    public CompletableFuture&amp;lt;CustomerProfile&amp;gt; getCustomer(String customerId) {
        return CompletableFuture.supplyAsync(() -&amp;gt; 
            restTemplate.getForObject(&quot;/api/customers/&quot; + customerId, CustomerProfile.class)
        );
    }
    
    private CompletableFuture&amp;lt;CustomerProfile&amp;gt; getCustomerFallback(String customerId, Exception ex) {
        log.warn(&quot;Customer service unavailable, using cached data&quot;);
        return CompletableFuture.completedFuture(
            customerCache.get(customerId)
                .orElseThrow(() -&amp;gt; new ServiceUnavailableException(&quot;No cached data&quot;, ex))
        );
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Configuration:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;CircuitBreakerConfig.custom()
    .failureRateThreshold(50)          // Open if 50% fail
    .waitDurationInOpenState(Duration.ofSeconds(30))
    .slidingWindowSize(100)            // Consider last 100 calls
    .slowCallDurationThreshold(Duration.ofSeconds(2))
    .build();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;5-structured-logging-and-distributed-tracing&quot;&gt;5. Structured Logging and Distributed Tracing&lt;/h3&gt;
&lt;p&gt;You can't fix what you can't see:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Slf4j
@RestController
public class SubscriptionController {
    
    @PostMapping(&quot;/subscriptions&quot;)
    public ResponseEntity&amp;lt;SubscriptionResponse&amp;gt; enrollMember(
            @RequestBody EnrollmentRequest request,
            @RequestHeader(&quot;X-Request-ID&quot;) String requestId) {
        
        MDC.put(&quot;requestId&quot;, requestId);
        MDC.put(&quot;customerId&quot;, request.getCustomerId());
        
        try {
            log.info(&quot;Starting enrollment&quot;, 
                kv(&quot;tier&quot;, request.getTier()),
                kv(&quot;paymentMethod&quot;, request.getPaymentMethod().getType()));
            
            Instant start = Instant.now();
            EnrollmentResult result = enrollmentService.enroll(request);
            
            log.info(&quot;Enrollment completed&quot;,
                kv(&quot;subscriptionId&quot;, result.getSubscriptionId()),
                kv(&quot;durationMs&quot;, Duration.between(start, Instant.now()).toMillis()));
            
            return ResponseEntity.ok(toResponse(result));
        } finally {
            MDC.clear();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Logging standards:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use structured key-value pairs, not string concatenation&lt;/li&gt;
&lt;li&gt;Track request IDs across service boundaries&lt;/li&gt;
&lt;li&gt;Include customer ID and operation type in every log&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;6-database-query-optimization&quot;&gt;6. Database Query Optimization&lt;/h3&gt;
&lt;p&gt;Slow queries kill scalability:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Entity
@Table(name = &quot;subscriptions&quot;, indexes = {
    @Index(name = &quot;idx_customer_id&quot;, columnList = &quot;customer_id&quot;),
    @Index(name = &quot;idx_tier_status&quot;, columnList = &quot;tier, status&quot;),
    @Index(name = &quot;idx_expires_at&quot;, columnList = &quot;expires_at&quot;)
})
public class Subscription {
    // Use @BatchSize to prevent N+1 queries
    @OneToMany(mappedBy = &quot;subscription&quot;, fetch = FetchType.LAZY)
    @BatchSize(size = 50)
    private List&amp;lt;Benefit&amp;gt; benefits;
}

@Repository
public interface SubscriptionRepository extends JpaRepository&amp;lt;Subscription, String&amp;gt; {
    
    // Custom query with fetch join to avoid N+1
    @Query(&quot;SELECT s FROM Subscription s LEFT JOIN FETCH s.benefits WHERE s.customerId = :customerId&quot;)
    Optional&amp;lt;Subscription&amp;gt; findByCustomerIdWithBenefits(@Param(&quot;customerId&quot;) String customerId);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Performance lessons:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Index everything you query by&lt;/li&gt;
&lt;li&gt;Use batch fetching to prevent N+1 queries&lt;/li&gt;
&lt;li&gt;Paginate large results&lt;/li&gt;
&lt;li&gt;Monitor and alert on queries &amp;gt;100ms&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;7-connection-pool-tuning&quot;&gt;7. Connection Pool Tuning&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;spring:
  datasource:
    hikari:
      # Formula: (core_count * 2) + effective_spindle_count
      # For 4-core with SSD: (4 * 2) + 1 = 9
      maximum-pool-size: 10
      minimum-idle: 5
      connection-timeout: 3000
      validation-timeout: 1000
      leak-detection-threshold: 60000
      max-lifetime: 1800000  # 30 minutes
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;8-zero-downtime-deployments&quot;&gt;8. Zero-Downtime Deployments&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# kubernetes/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: subscription-service
spec:
  replicas: 6
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 3        # Add 50% extra during update
      maxUnavailable: 1  # Keep 5/6 running
  template:
    spec:
      containers:
      - name: subscription-service
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;9-metrics-that-matter&quot;&gt;9. Metrics That Matter&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Service
public class MetricsService {
    
    private final Counter enrollmentsCounter;
    private final Timer enrollmentDuration;
    
    public MetricsService(MeterRegistry registry) {
        this.enrollmentsCounter = Counter.builder(&quot;subscription.enrollments&quot;)
            .tag(&quot;service&quot;, &quot;subscription&quot;)
            .register(registry);
            
        this.enrollmentDuration = Timer.builder(&quot;subscription.enrollment.duration&quot;)
            .publishPercentiles(0.5, 0.95, 0.99)
            .register(registry);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Monitor:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Business: Enrollments/hour, conversion rate, revenue&lt;/li&gt;
&lt;li&gt;Technical: P50/P95/P99 latency, error rate, throughput&lt;/li&gt;
&lt;li&gt;Infrastructure: CPU, memory, connection pool utilization&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;10-testing-strategy&quot;&gt;10. Testing Strategy&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// Unit tests - fast, isolated
@Test
void shouldCalculateCorrectExpirationDate() {
    EnrollmentService service = new EnrollmentService(
        mock(CustomerClient.class),
        mock(PaymentClient.class),
        mock(SubscriptionRepository.class)
    );
    
    Instant enrolledAt = Instant.parse(&quot;2024-01-01T00:00:00Z&quot;);
    Instant expected = Instant.parse(&quot;2025-01-01T00:00:00Z&quot;);
    
    assertEquals(expected, service.calculateExpiration(enrolledAt, SubscriptionTier.ANNUAL));
}

// Integration tests - with real database
@SpringBootTest
@Testcontainers
class SubscriptionServiceIntegrationTest {
    
    @Container
    static PostgreSQLContainer&amp;lt;?&amp;gt; postgres = new PostgreSQLContainer&amp;lt;&amp;gt;(&quot;postgres:15&quot;);
    
    @Test
    void shouldPersistAndRetrieveSubscription() {
        EnrollmentResult result = service.enroll(request);
        Optional&amp;lt;Subscription&amp;gt; retrieved = service.getSubscription(result.getSubscriptionId());
        
        assertTrue(retrieved.isPresent());
        assertEquals(SubscriptionTier.BASIC, retrieved.get().getTier());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;key-takeaways&quot;&gt;Key Takeaways&lt;/h3&gt;
&lt;p&gt;After building and scaling microservices handling millions of requests:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Design for failure&lt;/strong&gt; - Circuit breakers, timeouts, and fallbacks are not optional&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embrace eventual consistency&lt;/strong&gt; - Not everything needs to be synchronous&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Observability is critical&lt;/strong&gt; - You can't improve what you can't measure&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache aggressively&lt;/strong&gt; - But invalidate intelligently&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test at every level&lt;/strong&gt; - Unit, integration, contract, and load tests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automate deployments&lt;/strong&gt; - Including rollbacks and scaling&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance is a feature&lt;/strong&gt; - Optimize from day one&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;what-s-next--1&quot;&gt;What's Next?&lt;/h3&gt;
&lt;p&gt;In future posts, I'll dive deeper into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Advanced observability with distributed tracing&lt;/li&gt;
&lt;li&gt;Chaos engineering and resilience testing&lt;/li&gt;
&lt;li&gt;Cost optimization for cloud deployments&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Questions or want to discuss these patterns?&lt;/strong&gt; Leave a comment below or connect with me on &lt;a rel=&quot;nofollow&quot; href=&quot;https://linkedin.com/in/vamsithatikonda&quot;&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; #spring-boot #microservices #java #system-design #aws #architecture #scalability #production-engineering&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/11476/building-production-ready-spring-boot-microservices-lessons-from-scaling-to-millions</guid>
<pubDate>Thu, 12 Feb 2026 05:38:38 +0000</pubDate>
</item>
<item>
<title>The Hardware Mutex: Binding LLM Context to a TPM Root of Trust</title>
<link>https://coderlegion.com/11007/the-hardware-mutex-binding-llm-context-to-a-tpm-root-of-trust</link>
<description>&lt;p&gt;&lt;strong&gt;The Vulnerability:&lt;/strong&gt; &lt;code&gt;cat /proc/$PID/mem&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We need to stop pretending that API Keys are &quot;Identity.&quot;&lt;/p&gt;
&lt;p&gt;If you are building an Autonomous Agent today, you are likely storing its credentials (OAuth tokens, PEM keys) in memory. This works fine for a stateless chatbot. It is catastrophic for an Agent authorized to move money.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Attack Vector is trivial:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;An attacker gains root access (or compromised container privileges).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They run a memory dump (&lt;code&gt;gcore&lt;/code&gt; or just reading &lt;code&gt;/proc/mem&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They &lt;code&gt;grep&lt;/code&gt; for the &lt;code&gt;Bearer&lt;/code&gt; token.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They clone the Agent’s identity and empty the wallet.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Software permissions (RBAC) cannot solve this because Root is God. If the operating system trusts the Admin, the Admin can impersonate the Agent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix: The Hardware Mutex&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To secure high-stakes Agents, we must move from &lt;strong&gt;Software Identity&lt;/strong&gt; (which can be copied) to &lt;strong&gt;Hardware Identity&lt;/strong&gt; (which must be proved).&lt;/p&gt;
&lt;p&gt;I’ve released a reference architecture called &lt;strong&gt;The Citadel Protocol&lt;/strong&gt; that defines how to implement this using a &lt;strong&gt;Hardware Mutex&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here are the mechanics of the stack:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Enclave (The Body)&lt;/strong&gt;&lt;br&gt;
The Agent does not run in a standard Docker container. It runs inside a &lt;strong&gt;TEE (Trusted Execution Environment)&lt;/strong&gt; — like AWS Nitro Enclaves or Intel SGX.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The Physics:&lt;/strong&gt; The memory space is encrypted at the hardware level. Even the Hypervisor (AWS/Azure) cannot read the RAM.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Attestation (The Heartbeat)&lt;/strong&gt;&lt;br&gt;
Instead of a static API Key, the Agent authenticates using Remote Attestation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Boot:&lt;/strong&gt; On startup, the TPM measures the hash of the kernel and the application code into a Platform Configuration Register (&lt;strong&gt;PCR0&lt;/strong&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Quote:&lt;/strong&gt; When the Agent wants to &quot;act&quot; (e.g., call an API), it asks the TPM to sign a &quot;Quote&quot; containing the PCR values.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Verify:&lt;/strong&gt; The upstream service verifies the Quote. If the code has been altered (even by one byte), the hash changes, the signature fails, and the connection is dropped.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Witness (The Ledger)&lt;/strong&gt;&lt;br&gt;
We stop logging to text files (which can be &lt;code&gt;rm -rf&lt;/code&gt;'d). We log to a &lt;strong&gt;Merkle Tree&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every action the Agent takes is hashed and appended to a write-only chain.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The head of the chain is anchored externally.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This makes the execution history mathematically impossible to alter&lt;br&gt;
without breaking the chain.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The Spec&lt;/strong&gt;&lt;br&gt;
We are entering the era where &quot;Code&quot; is no longer enough. We need &quot;Code + Physics.&quot;&lt;/p&gt;
&lt;p&gt;I have open-sourced the full architectural specification for this topology. It covers the specific PCR measurements, the Witness schemas, and the &quot;Terminal Refusal&quot; logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Read the full Citadel Protocol Specification (Zenodo): &lt;a rel=&quot;nofollow&quot; href=&quot;https://zenodo.org/records/18472859&quot;&gt;https://zenodo.org/records/18472859&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/11007/the-hardware-mutex-binding-llm-context-to-a-tpm-root-of-trust</guid>
<pubDate>Tue, 03 Feb 2026 20:43:43 +0000</pubDate>
</item>
<item>
<title>What exactly is a &quot;program&quot; and what does it include?</title>
<link>https://coderlegion.com/6539/what-exactly-is-a-program-and-what-does-it-include</link>
<description>&lt;p&gt;In this tutorial, I wanna talk about &quot;program&quot; and &quot;process&quot; terms that are fundamental elements in computer-science. This two terms generally are used exchangeably. But in technically, both are so different. Let's begin with explaining these terms and then deeply see the what it includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;program&lt;/strong&gt; is a file containing a range of information that describes how to construct a &quot;process&quot; at run time [1].&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;process&lt;/strong&gt; is an instance of an executing program [1].&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are the formal definations. If I try to express with my words: &lt;/p&gt;
&lt;p&gt;A program (or binary) is a file that includes the machine code + metadata + debug information (if the program compiled with &lt;em&gt;-g&lt;/em&gt; flag) and process is a abstraction point that kernel creates and then allocates hardware resources like RAM.&lt;/p&gt;
&lt;p&gt;First thing that you should know is the program format. In recently, there are two type of program formats that the compilers generate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ELF (Executable and Linkable Format)&lt;/li&gt;
&lt;li&gt;PE (Portable Executable)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;elf-format&quot;&gt;ELF Format&lt;/h3&gt;
&lt;p&gt;ELF is the standard format for UNIX/Linux systems and PE for Windows. I'm currently on Ubuntu 24.10 (x86_64) so that I will explain and use ELF format. As you guest, I don't know really PE format. But there is a good reference handbook that you can look at. It's &quot;Practical Binary Analysis&quot; written by Dennis Andriesse.&lt;/p&gt;
&lt;p&gt;After explained the program formats, right now, let's look at the inside of the program:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=2197297415558036422&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;In general, any program includes these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Executable header&lt;/li&gt;
&lt;li&gt;Program headers&lt;/li&gt;
&lt;li&gt;Sections&lt;/li&gt;
&lt;li&gt;Section headers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Every ELF file starts with an &lt;strong&gt;executable header&lt;/strong&gt;, which is just a structured series of bytes telling you that it's and ELF file, what kind of ELF file it is, and where in the files to find all the other contents [2].&lt;/p&gt;
&lt;p&gt;You can exactly see the content of this header with &lt;strong&gt;readelf&lt;/strong&gt; command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ readelf -h ./copy
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Position-Independent Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x1160
  Start of program headers:          64 (bytes into file)
  Start of section headers:          17496 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         13
  Size of section headers:           64 (bytes)
  Number of section headers:         37
  Section header string table index: 36
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In there, first four digits (7f 45) of &lt;em&gt;Magic&lt;/em&gt; series define the program format. You will see the probably different series if you are on Windows. Other properties is the self-explaining.&lt;/p&gt;
&lt;p&gt;The code and data in an ELF program are logically divided into &lt;strong&gt;sections&lt;/strong&gt;. Each section includes a specific part of your source code. And the sections in the binary are contained in the &lt;strong&gt;section header table&lt;/strong&gt;.  Some sections are used to execute the machine instructions and some for other information (like symbol table used by debugger). Let's look at the section headers:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ readelf --sections --wide ./copy
There are 37 section headers, starting at offset 0x4458:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        0000000000000318 000318 00001c 00   A  0   0  1
  [ 2] .note.gnu.property NOTE            0000000000000338 000338 000030 00   A  0   0  8
  [ 3] .note.gnu.build-id NOTE            0000000000000368 000368 000024 00   A  0   0  4
  [ 4] .note.ABI-tag     NOTE            000000000000038c 00038c 000020 00   A  0   0  4
  [ 5] .gnu.hash         GNU_HASH        00000000000003b0 0003b0 000028 00   A  6   0  8
  [ 6] .dynsym           DYNSYM          00000000000003d8 0003d8 000180 18   A  7   1  8
  [ 7] .dynstr           STRTAB          0000000000000558 000558 0000d3 00   A  0   0  1
  [ 8] .gnu.version      VERSYM          000000000000062c 00062c 000020 02   A  6   0  2
  [ 9] .gnu.version_r    VERNEED         0000000000000650 000650 000030 00   A  7   1  8
  [10] .rela.dyn         RELA            0000000000000680 000680 0000d8 18   A  6   0  8
  [11] .rela.plt         RELA            0000000000000758 000758 0000d8 18  AI  6  24  8
  [12] .init             PROGBITS        0000000000001000 001000 00001b 00  AX  0   0  4
  [13] .plt              PROGBITS        0000000000001020 001020 0000a0 10  AX  0   0 16
  [14] .plt.got          PROGBITS        00000000000010c0 0010c0 000010 10  AX  0   0 16
  [15] .plt.sec          PROGBITS        00000000000010d0 0010d0 000090 10  AX  0   0 16
  [16] .text             PROGBITS        0000000000001160 001160 00043a 00  AX  0   0 16
  [17] .fini             PROGBITS        000000000000159c 00159c 00000d 00  AX  0   0  4
  [18] .rodata           PROGBITS        0000000000002000 002000 000040 00   A  0   0  4
  [19] .eh_frame_hdr     PROGBITS        0000000000002040 002040 000034 00   A  0   0  4
  [20] .eh_frame         PROGBITS        0000000000002078 002078 0000a8 00   A  0   0  8
  [21] .init_array       INIT_ARRAY      0000000000003d78 002d78 000008 08  WA  0   0  8
  [22] .fini_array       FINI_ARRAY      0000000000003d80 002d80 000008 08  WA  0   0  8
  [23] .dynamic          DYNAMIC         0000000000003d88 002d88 0001f0 10  WA  7   0  8
  [24] .got              PROGBITS        0000000000003f78 002f78 000088 08  WA  0   0  8
  [25] .data             PROGBITS        0000000000004000 003000 000010 00  WA  0   0  8
  [26] .bss              NOBITS          0000000000004020 003010 000010 00  WA  0   0 32
  [27] .comment          PROGBITS        0000000000000000 003010 00002b 01  MS  0   0  1
  [28] .debug_aranges    PROGBITS        0000000000000000 00303b 000030 00      0   0  1
  [29] .debug_info       PROGBITS        0000000000000000 00306b 000472 00      0   0  1
  [30] .debug_abbrev     PROGBITS        0000000000000000 0034dd 000184 00      0   0  1
  [31] .debug_line       PROGBITS        0000000000000000 003661 00017e 00      0   0  1
  [32] .debug_str        PROGBITS        0000000000000000 0037df 00031a 01  MS  0   0  1
  [33] .debug_line_str   PROGBITS        0000000000000000 003af9 00012f 01  MS  0   0  1
  [34] .symtab           SYMTAB          0000000000000000 003c28 000438 18     35  18  8
  [35] .strtab           STRTAB          0000000000000000 004060 00028c 00      0   0  1
  [36] .shstrtab         STRTAB          0000000000000000 0042ec 00016a 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), l (large), p (processor specific)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main topic of this tutorial is the sections. So I wanna explain in deeply each one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;.init&lt;/strong&gt; and &lt;strong&gt;.fini&lt;/strong&gt;: The machine code in these sections are used before/after the &lt;em&gt;main()&lt;/em&gt; function in program. Don't forget that we're not writing this sections. Compiler itself creates these to handle low-level (or hardware-specific) works. I don't know actually what both of them do!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.text&lt;/strong&gt;: It is the main area where we focus on binary analysis and reverse-engineering stuffs and includes the your code in machine instruction representation. If you look at there, you see that it has voluminous area and thousands of lines of machine code even if it is small program. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.bss&lt;/strong&gt;, &lt;strong&gt;.data&lt;/strong&gt; and &lt;strong&gt;.rodata&lt;/strong&gt;: These sections are writable and used to hold various variable in source code. &lt;strong&gt;.bss&lt;/strong&gt; includes the &lt;em&gt;static&lt;/em&gt; and global &lt;em&gt;uninitialized&lt;/em&gt; and &lt;strong&gt;.data&lt;/strong&gt; includes the &lt;em&gt;static&lt;/em&gt; and global &lt;em&gt;initialized&lt;/em&gt; variables. Also &lt;strong&gt;.rodata&lt;/strong&gt; consists of variables defined with &lt;em&gt;const&lt;/em&gt; keyword in code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.dynamic&lt;/strong&gt;: This is the &quot;road map&quot; for the kernel and dynamic linker when loading and setting up an ELF binary for executions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.init_array&lt;/strong&gt; and &lt;strong&gt;.fini_array&lt;/strong&gt;: These contains an array of pointers to functions to use as constructors/destructors. You maybe know that how to create constructor and destructor using compiler specific-tool, line one &lt;em&gt;__attribute__((constructor)) void run_before_main(void);&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.shstrtab&lt;/strong&gt;, &lt;strong&gt;.symtab&lt;/strong&gt;, &lt;strong&gt;.strtab&lt;/strong&gt;, &lt;strong&gt;.dynsym&lt;/strong&gt; and &lt;strong&gt;.dynstr&lt;/strong&gt;: The &lt;strong&gt;.shstrtab&lt;/strong&gt; section is simply an array of NULL-terminated strings that contain the names of all the sections in binary. The &lt;strong&gt;.symtab&lt;/strong&gt; contains a symbol table and &lt;strong&gt;.strtab&lt;/strong&gt; contains the symbolic names. The &lt;strong&gt;.dynsym&lt;/strong&gt; and &lt;strong&gt;.dynstr&lt;/strong&gt; are analogous to &lt;strong&gt;.symtab&lt;/strong&gt; and &lt;strong&gt;.strtab&lt;/strong&gt;, except that they contain symbols and strings needed for dynamic linking rather than static linking.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.debug_&lt;/strong&gt;: These sections are used primarily by debugger (GDB) so that it has not include and machine code but just metadata about program that debugger can use later. If you don't compile the program with &lt;em&gt;-g&lt;/em&gt; option, these sections will not there.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below is a part of .text section:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ objdump -j .text -d ./copy

./copy:     file format elf64-x86-64

(...)

Disassembly of section .text:

0000000000001249 &amp;lt;main&amp;gt;:
    1249:f3 0f 1e fa          endbr64
    124d:55                   push   %rbp
    124e:48 89 e5             mov    %rsp,%rbp
    1251:48 81 ec 40 04 00 00 sub    $0x440,%rsp
    1258:89 bd cc fb ff ff    mov    %edi,-0x434(%rbp)
    125e:48 89 b5 c0 fb ff ff mov    %rsi,-0x440(%rbp)
    1265:64 48 8b 04 25 28 00 mov    %fs:0x28,%rax
    126c:00 00 
    126e:48 89 45 f8          mov    %rax,-0x8(%rbp)
    1272:31 c0                xor    %eax,%eax
    1274:83 bd cc fb ff ff 03 cmpl   $0x3,-0x434(%rbp)
    127b:75 24                jne    12a1 &amp;lt;main+0x58&amp;gt;
    127d:48 8b 85 c0 fb ff ff mov    -0x440(%rbp),%rax
    1284:48 83 c0 08          add    $0x8,%rax
    1288:48 8b 00             mov    (%rax),%rax
    128b:48 8d 15 72 0d 00 00 lea    0xd72(%rip),%rdx        # 2004 &amp;lt;_IO_stdin_used+0x4&amp;gt;

(...)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In here, we see the three columns that show the machine instructions of your program. At left side, you see the addresses of machine instructions. When running the program, stack pointer tracks these addresses. At center, you see the actual machine instructions corresponding to your code. Program counter register tracks this machine instructions. At right side, you see the assembly-level representations. When dealing with reverse-engineering, we inspect these to understand what program does.&lt;/p&gt;
&lt;h3 id=&quot;virtual-memory-layout&quot;&gt;Virtual Memory Layout&lt;/h3&gt;
&lt;p&gt;After discussed the ELF program format, right now, I will explain the virtual memory layout of a process (running program instance). When you run the program, kernel creates a memory layout as below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=13211328505900074658&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I've explained the &lt;strong&gt;.text&lt;/strong&gt;, &lt;strong&gt;.data&lt;/strong&gt;, &lt;strong&gt;.bss&lt;/strong&gt;, &lt;strong&gt;.rodata&lt;/strong&gt; sections previously. Apart from these, you see the &lt;strong&gt;stack&lt;/strong&gt; and &lt;strong&gt;heap&lt;/strong&gt; areas. &lt;/p&gt;
&lt;p&gt;Both are the memory areas that kernel uses to execute the machine instructions. Stack area has LIFO (Last-In First-Out) model. When executing the program, kernel &lt;em&gt;pushs&lt;/em&gt;/&lt;em&gt;pops&lt;/em&gt; the instructions in here. So it grows up to lower address space. Heap area is completely different! It has FIFO (First-In First-Out) model. Kernel uses this memory space for dynamic allocation with &lt;strong&gt;malloc()&lt;/strong&gt;/&lt;strong&gt;free()&lt;/strong&gt; function family. And it grows up to upper address space. The heap address border is claimed by &lt;strong&gt;brk()&lt;/strong&gt; function.&lt;/p&gt;
&lt;p&gt;Until here, I wanna give you a general overview about programs and processes. If you want to dive in more, you can check and look at the below references.&lt;/p&gt;
&lt;p&gt;[1]. Kerrisk M., The Linux Programming Interface, no starch press, San Francisco, page 113.&lt;/p&gt;
&lt;p&gt;[2]. Andriesse D., Practical Binary Analysis, no starch press, San Francisco, page 33.&lt;/p&gt;
</description>
<category>Getting Started</category>
<guid isPermaLink="true">https://coderlegion.com/6539/what-exactly-is-a-program-and-what-does-it-include</guid>
<pubDate>Thu, 16 Oct 2025 20:24:57 +0000</pubDate>
</item>
<item>
<title>Understanding embedded flashing and debugging</title>
<link>https://coderlegion.com/6415/understanding-embedded-flashing-and-debugging</link>
<description>&lt;p&gt;In embedded world, using IDE (like Keil and STM32Cube IDE) is  heavily the preferred choice because of simplifying the all processes. But I think that we, embedded software developers, must learn and know what is going on under the hood!&lt;/p&gt;
&lt;p&gt;In this articles, I'll show the all the steps of flashing and debugging a firmware written for stm32f446x series microcontroller. Firstly, I'll introduce the flashing a firmware into microcontroller and then give you how to debug it.&lt;/p&gt;
&lt;p&gt;Before giving details, let's talk about what we need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Linux terminal (Ubuntu terminal)&lt;/li&gt;
&lt;li&gt;A firmware (ended with &lt;em&gt;.elf&lt;/em&gt; or &lt;em&gt;.bin&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;ARM GCC toolchain (&lt;em&gt;arm-none-eabi-&lt;/em&gt; commands)&lt;/li&gt;
&lt;li&gt;Open On-Chip Debugger (&lt;em&gt;openocd&lt;/em&gt; command)&lt;/li&gt;
&lt;li&gt;ST-Link hardware&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please get these before going further according to your system. I'm current on Ubuntu 22.04.&lt;/p&gt;
&lt;p&gt;When talking about a embedded firmware, we have two program formats that we use in general. These are .bin and .elf formats. Ended with .bin firmware just contains the pure machine code and it has not debug information, program sections or other stuffs. It is used just for flashing, not for debugging. Beside that .elf firmware contains both pure machine code and other stuffs so that we can flash and debug it. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Please use the firmware ended with .elf&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Another important point is the connection type between your host and microcontroller. For ARM Cortex, we have a few options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;openocd&lt;/li&gt;
&lt;li&gt;dfu-util&lt;/li&gt;
&lt;li&gt;st-flash&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have ST-Link connection so that openocd is preferred way in our case. If you have USB connection, you can use dfu-util. Most of the STM32 microcontrollers support DFU (Device Firmware Update) mode. Don't forget that you can just flash the firmware over USB, not for debugging. &lt;/p&gt;
&lt;p&gt;All right, let's dive into flashing.&lt;/p&gt;
&lt;p&gt;First of all, open a terminal and type this command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ openocd -f interface/stlink.cfg -f target/stm32f4x.cfg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In here, we specified two files. These are &lt;em&gt;interface&lt;/em&gt; (which connection type that I use?) and &lt;em&gt;target&lt;/em&gt; (what type of microcontroller that I wanna flash and debug). You can see all interface and target files from openocd folder that you've installed.&lt;/p&gt;
&lt;p&gt;After typed this command, you get:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Open On-Chip Debugger 0.12.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport &quot;hla_swd&quot;. To override use 'transport select &amp;lt;transport&amp;gt;'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 2000 kHz
Info : STLINK V2J33M25 (API v2) VID:PID 0483:374B
Info : Target voltage: 3.228402
Info : [stm32f4x.cpu] Cortex-M4 r0p1 processor detected
Info : [stm32f4x.cpu] target has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f4x.cpu on 3333
Info : Listening on port 3333 for gdb connections
[stm32f4x.cpu] halted due to breakpoint, current mode: Thread 
xPSR: 0x61000000 pc: 0x08000530 msp: 0x2001ffd8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can think that openocd is a bridge between your host and microcontroller. To flash and debug the firmware, we'll use arm-none-eabi-gdb command. It is actually a debugger for ARM Cortex. But we can flash too.&lt;/p&gt;
&lt;p&gt;Right now, open a new terminal and type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ arm-none-eabi-gdb firmware.elf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that you are in gdb session and we will make all operations from here. The first thing that we wanna is to connect to somewhere. &lt;/p&gt;
&lt;p&gt;From openocd output, we see the &quot;Info : Listening on port 3333 for gdb connections&quot; line. As I said before, openocd is a bridge and created a port 3333 for gdb that we will connect there from gdb session. Let's do it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(gdb) target remote localhost:3333
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, you see the first line of &lt;em&gt;main()&lt;/em&gt; from the firmware. It's time to flash the firmware and then inspect it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(gdb) monitor reset halt
(gdb) load
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Important note is that before flashing the firmware, resetting the microcontroller and halting the current execution flow is good practice. After these commands, you should see:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Loading section .isr_vector, size 0x1c4 lma 0x8000000
Loading section .text, size 0x1080 lma 0x80001d0
Loading section .rodata, size 0x10 lma 0x8001250
Loading section .init_array, size 0x4 lma 0x8001260
Loading section .fini_array, size 0x4 lma 0x8001264
Loading section .data, size 0x10 lma 0x8001268
Start address 0x80011d0, load size 4716
Transfer rate: 9 KB/sec, 786 bytes/write.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Congrats! You finally flashed the firmware. &lt;/p&gt;
&lt;p&gt;In here, you see that many sections written to base address of flash memory (0x08000000). I will not make end-to-end binary analysis but let's explain basically what these sections are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;.isr_vector&lt;/strong&gt; --&amp;gt; IRS (Interrupt Service Routine) handlers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.text&lt;/strong&gt; --&amp;gt; machine code that I've written&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.rodata&lt;/strong&gt; --&amp;gt; read-only variable declarations (with &lt;em&gt;const&lt;/em&gt; in C)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.init_array&lt;/strong&gt;, &lt;strong&gt;.fini_array&lt;/strong&gt; --&amp;gt; hardware-level machine code that I've not written&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.data&lt;/strong&gt; --&amp;gt; &lt;em&gt;static&lt;/em&gt; or global initialized variables (if exists)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.bss&lt;/strong&gt; --&amp;gt; &lt;em&gt;static&lt;/em&gt; or global uninitialized variables (if exists)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are the main blocks/sections in any sort of program. You can inspect these in deeply with &lt;em&gt;arm-none-eabi-objdump&lt;/em&gt; and &lt;em&gt;arm-none-eabi-readelf&lt;/em&gt; commands. But for now, let's go with debugging.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The main purpose of debugging is to track the execution of firmware and, sometimes, extract the any possible information about firmware.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can look at these information over &lt;em&gt;info&lt;/em&gt; commands. Some examples are below:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(gdb) info files
(gdb) info registers
(gdb) info functions
(gdb) info variables
(gdb) info locals
(gdb) info frame
(gdb) info breakpoints
(gdb) info line *addr
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;em&gt;list&lt;/em&gt; command, you can see the part of source file from gdb session:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(gdb) list 1
1/**
2 * FreeRTOS 101 - Complete Introductory Program
3 */
4
5#include &quot;main.h&quot;
6
7void TaskLED(void *params)
8{
9while (1) {
10HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After gaining a overview about the firmware, next step is to track the execution flow. To control the execution flow, we have &lt;em&gt;break&lt;/em&gt;, &lt;em&gt;continue&lt;/em&gt;, &lt;em&gt;step&lt;/em&gt;, &lt;em&gt;next&lt;/em&gt;, &lt;em&gt;finish&lt;/em&gt; commands:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(gdb) break main                # put the breakpoint at begin of main() function
(gdb) break *0x80001d0   # put the breakpoint at 0x080001d0 address
(gdb) delete                         # delete all breakpoints
(gdb) continue                    # continue the execution flow until any breakpoint
(gdb) next                            # go to next instruction (over functions)
(gdb) step                            # go to next instruction (into functions)
(gdb) finish                          # finish the execution flow
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When dealing with embedded systems, we probably want to look at (and set) core and peripheral registers or variables so that we gain the low-level insights about it. For this we have &lt;em&gt;print&lt;/em&gt;, &lt;em&gt;x/&lt;/em&gt;, &lt;em&gt;set&lt;/em&gt; commands. We use &lt;em&gt;print&lt;/em&gt; for displaying any variable defined in source code, &lt;em&gt;x/&lt;/em&gt; for displaying registers, &lt;em&gt;set&lt;/em&gt; for setting register bits:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(gdb) print initLED                 # display the GPIO initialization structure
(gdb) x/x 0x40023830          # look at RCC AHB1 peripheral clock enable register in hex
(gdb) x/d 0x40023830          # look at RCC AHB1 peripheral clock enable register in decimal
(gdb) x/s 0x40023830          # look at RCC AHB1 peripheral clock enable register in string
(gdb) x/10wx 0x40023830   # look at RCC AHB1 peripheral clock enable with next 9 word (32-bit) registers
(gdb) set {int32_t}0x40023830 |= (1 &amp;lt;&amp;lt; 2)   # set the third bit of RCC AHB1 peripheral clock enable register
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Well done! This is 80% of debugging. &lt;/p&gt;
&lt;p&gt;Until now, I've tried to give you general workflow and commands about flashing and debugging the firmware. But as you guest, this is deep and complex area so that it deserves many articles. For now, I think that it is enough!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/6415/understanding-embedded-flashing-and-debugging</guid>
<pubDate>Tue, 14 Oct 2025 11:07:59 +0000</pubDate>
</item>
<item>
<title>Foreign Keys in MySQL for Serious System Design</title>
<link>https://coderlegion.com/6029/foreign-keys-in-mysql-for-serious-system-design</link>
<description>&lt;p&gt;Let’s face it: encountering foreign keys in MySQL is a classic developer rite of passage. At first, it's about linking tables—but swiftly turns into crafting a bulletproof web of relationships and rules that underpin entire data-driven systems. But at scale, these &lt;em&gt;seemingly simple constraints&lt;/em&gt; form the backbone of reliable, maintainable, and secure architectures.&lt;/p&gt;
&lt;p&gt;This isn’t just a guide to syntax. This is your &lt;strong&gt;comprehensive field manual&lt;/strong&gt; for wielding foreign keys: strategies, subtle caveats, best practices, and even architectural patterns favored by pros.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.mysqltutorial.org/wp-content/uploads/2019/08/customers-orders.png&quot; alt=&quot;MYSQL&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;quick-refresher-what-are-foreign-keys-&quot;&gt;&lt;strong&gt;Quick Refresher: What are Foreign Keys?&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;A &lt;strong&gt;foreign key&lt;/strong&gt; is a column or set of columns in one table that references the primary key in another table, enforcing relationship integrity between records. Without them, you risk “orphan” data—think: orders without matching customers, employees reporting to non-existent managers, or posts referencing gone users.&lt;/p&gt;
&lt;h5 id=&quot;example-&quot;&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;advanced-foreign-key-tactics&quot;&gt;&lt;strong&gt;Advanced Foreign Key Tactics&lt;/strong&gt;&lt;/h4&gt;
&lt;h5 id=&quot;1-cascade-operations-controlling-your-data-s-life-cycle&quot;&gt;&lt;strong&gt;1. Cascade Operations: Controlling Your Data’s Life Cycle&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;The magic of MySQL’s foreign keys isn’t just the linking—it's about controlling &lt;em&gt;what happens&lt;/em&gt; on updates or deletes. Choose from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;CASCADE&lt;/code&gt;&lt;/strong&gt;: Mods or deletes in the parent auto-propagate to children. Delete a customer? Their orders vanish too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;SET NULL&lt;/code&gt;&lt;/strong&gt;: Leaves child reference as &lt;code&gt;NULL&lt;/code&gt; if parent goes away—if your business logic allows “detached” records.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;RESTRICT&lt;/code&gt;&lt;/strong&gt;/&lt;code&gt;NO ACTION&lt;/code&gt;: Block changes if dependencies remain. Most conservative.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;When to Use What?&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Operation    &lt;/th&gt;&lt;th&gt;Use Case Example                                                                 &lt;/th&gt;&lt;th&gt;Beware...&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;CASCADE      &lt;/td&gt;&lt;td&gt;Orders deleted when customer is deleted                                          &lt;/td&gt;&lt;td&gt;Unintended mass deletions, data loss&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;SET NULL     &lt;/td&gt;&lt;td&gt;Product discontinued, but not deleting sale history                              &lt;/td&gt;&lt;td&gt;Nullability: Foreign key columns must allow it&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;RESTRICT     &lt;/td&gt;&lt;td&gt;Employees can’t be deleted if they manage teams                                  &lt;/td&gt;&lt;td&gt;Can hinder batch deletes/updates&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h5 id=&quot;2-many-to-many-amp-junction-tables&quot;&gt;&lt;strong&gt;2. Many-to-Many &amp;amp; Junction Tables&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;Not all relationships are one-to-many. &lt;strong&gt;Many-to-many&lt;/strong&gt; needs a &lt;em&gt;junction&lt;/em&gt; (associative) table with two or more foreign keys.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example: Users and Roles&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE user_roles (
    user_id INT,
    role_id INT,
    PRIMARY KEY(user_id, role_id),
    FOREIGN KEY (user_id) REFERENCES users(user_id),
    FOREIGN KEY (role_id) REFERENCES roles(role_id)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prevents a user from having invalid roles and vice-versa.&lt;/p&gt;
&lt;hr&gt;
&lt;h5 id=&quot;3-self-referential-amp-hierarchical-relationships&quot;&gt;&lt;strong&gt;3. Self-Referential &amp;amp; Hierarchical Relationships&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;Foreign keys can reference the &lt;em&gt;same&lt;/em&gt; table (think: management hierarchy or tree categories):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    manager_id INT,
    FOREIGN KEY (manager_id) REFERENCES employees(employee_id)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This enables recursive queries and elegant modeling of organizational trees.&lt;/p&gt;
&lt;hr&gt;
&lt;h5 id=&quot;4-deferrable-constraints-not-native-in-mysql-yet-&quot;&gt;&lt;strong&gt;4. Deferrable Constraints (Not Native in MySQL Yet)&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;Some databases let you defer foreign key checks till commit. MySQL doesn't natively support this (as of v8.0), but it’s crucial to know if you’re porting designs—sometimes, you need to temporarily violate constraints in multi-step transactions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Workaround&lt;/strong&gt;: Drop constraints, perform bulk actions, re-add and validate—&lt;em&gt;dangerous if not careful&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h5 id=&quot;5-foreign-key-indexing-for-performance&quot;&gt;&lt;strong&gt;5. Foreign Key Indexing for Performance&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;Every foreign key column, &lt;strong&gt;especially in large tables&lt;/strong&gt;, must be indexed! MySQL often creates indexes automatically, but manual indexing ensures optimal joins, cascades, and validation efficiency.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_orders_customer_id ON orders(customer_id);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;potential-pitfalls-amp-best-practices&quot;&gt;&lt;strong&gt;Potential Pitfalls &amp;amp; Best Practices&lt;/strong&gt;&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Pitfall                 &lt;/th&gt;&lt;th&gt;Impact/Example                                       &lt;/th&gt;&lt;th&gt;Best Practices&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Mismatched Datatypes    &lt;/td&gt;&lt;td&gt;INT in one, BIGINT in another = constraint fails     &lt;/td&gt;&lt;td&gt;Match PK and FK datatypes exactly&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Circular Dependencies   &lt;/td&gt;&lt;td&gt;Table A FK B, B FK A --&amp;gt; deadlocks, migrations pain &lt;/td&gt;&lt;td&gt;Avoid unless model strictly requires; batch disables&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Large Cascading Deletes &lt;/td&gt;&lt;td&gt;Deletes millions unintentionally                     &lt;/td&gt;&lt;td&gt;Use &lt;code&gt;RESTRICT&lt;/code&gt; or disable, and log deletes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Orphaned Data           &lt;/td&gt;&lt;td&gt;Data in child table with no valid parent             &lt;/td&gt;&lt;td&gt;Enforce referential integrity via FKs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Bulk Data Loads         &lt;/td&gt;&lt;td&gt;INSERTs/LOADs slower due to constraint checks        &lt;/td&gt;&lt;td&gt;Temporarily disable FKs during trusted imports&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h4 id=&quot;beyond-basics-multi-table-and-polymorphic-relations&quot;&gt;&lt;strong&gt;Beyond Basics: Multi-Table and Polymorphic Relations&lt;/strong&gt;&lt;/h4&gt;
&lt;h5 id=&quot;composite-foreign-keys&quot;&gt;&lt;strong&gt;Composite Foreign Keys&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;Sometimes, relationships are defined by &lt;em&gt;multiple columns&lt;/em&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE shipments (
    order_id INT,
    item_id INT,
    ...
    FOREIGN KEY (order_id, item_id) REFERENCES order_items(order_id, item_id)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;polymorphic-association-the-anti-pattern-&quot;&gt;&lt;strong&gt;Polymorphic Association (the Anti-pattern)&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;If you need a comment to belong to either a blog post or a photo—MySQL won’t let you set a FK to multiple tables. This is called a &lt;strong&gt;polymorphic association&lt;/strong&gt; and is discouraged in strict relational design—better to have a dedicated join table per association, or use inheritance-like strategies.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;enterprise-patterns-audit-trails-soft-deletes-and-data-warehousing&quot;&gt;&lt;strong&gt;Enterprise Patterns: Audit Trails, Soft Deletes, and Data Warehousing&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Soft Deletes&lt;/strong&gt;: Instead of deleting, mark as &lt;code&gt;deleted_at&lt;/code&gt;. &lt;strong&gt;Don’t cascade deletes&lt;/strong&gt; on these columns, handle logically.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audit Trails&lt;/strong&gt;: Log every change; foreign keys can help tie logs to users/actions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Warehousing / ETL&lt;/strong&gt;: Foreign keys are often dropped in warehouse schemas for performance—integrity must be enforced during loading (ETL).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;real-world-example-e-commerce-data-model-expanded&quot;&gt;&lt;strong&gt;Real-World Example: E-commerce Data Model—Expanded&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Tables:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Customers&lt;/code&gt; (&lt;code&gt;customer_id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Orders&lt;/code&gt; (&lt;code&gt;order_id&lt;/code&gt;, &lt;code&gt;customer_id&lt;/code&gt;, &lt;code&gt;order_date&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Products&lt;/code&gt; (&lt;code&gt;product_id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;category_id&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;, &lt;code&gt;stock_quantity&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Order_Items&lt;/code&gt; (&lt;code&gt;order_item_id&lt;/code&gt;, &lt;code&gt;order_id&lt;/code&gt;, &lt;code&gt;product_id&lt;/code&gt;, &lt;code&gt;quantity&lt;/code&gt;, &lt;code&gt;subtotal&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Categories&lt;/code&gt; (&lt;code&gt;category_id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;parent_category_id&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Reviews&lt;/code&gt; (&lt;code&gt;review_id&lt;/code&gt;, &lt;code&gt;product_id&lt;/code&gt;, &lt;code&gt;customer_id&lt;/code&gt;, &lt;code&gt;stars&lt;/code&gt;, &lt;code&gt;comment&lt;/code&gt;, &lt;code&gt;created_at&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Coupons&lt;/code&gt; (&lt;code&gt;coupon_id&lt;/code&gt;, &lt;code&gt;code&lt;/code&gt;, &lt;code&gt;discount_percent&lt;/code&gt;, &lt;code&gt;expires_at&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Order_Coupons&lt;/code&gt; (&lt;code&gt;order_id&lt;/code&gt;, &lt;code&gt;coupon_id&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Sample Foreign Key Usage:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;ALTER TABLE orders
    ADD CONSTRAINT fk_customer FOREIGN KEY (customer_id)
        REFERENCES customers(customer_id)
        ON DELETE RESTRICT;

ALTER TABLE order_items
    ADD CONSTRAINT fk_order FOREIGN KEY(order_id)
        REFERENCES orders(order_id)
        ON DELETE CASCADE, 
    ADD CONSTRAINT fk_product FOREIGN KEY(product_id)
        REFERENCES products(product_id)
        ON DELETE RESTRICT;

ALTER TABLE products
    ADD CONSTRAINT fk_category FOREIGN KEY(category_id)
        REFERENCES categories(category_id)
        ON DELETE SET NULL;

ALTER TABLE reviews
    ADD CONSTRAINT fk_review_product
        FOREIGN KEY (product_id) REFERENCES products(product_id)
        ON DELETE CASCADE,
    ADD CONSTRAINT fk_review_customer
        FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
        ON DELETE SET NULL;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Features Achieved:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Orders tied strictly to real customers; orphans impossible.&lt;/li&gt;
&lt;li&gt;Deleting a product deletes its reviews, but not historical order records.&lt;/li&gt;
&lt;li&gt;Deleting a category doesn’t nuke the products—only unclassifies them.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;foreign-keys-and-orm-mappings-advanced-&quot;&gt;&lt;strong&gt;Foreign Keys and ORM Mappings (Advanced)&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Using ORMs like SQLAlchemy, Hibernate, or Django ORM? Foreign keys are the link between your domain models. Properly defined, they:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enable cascading saves/deletes in code.&lt;/li&gt;
&lt;li&gt;Enable reverse lookups and eager/lazy loading.&lt;/li&gt;
&lt;li&gt;Ensure constraints enforced at both DB and code layer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Always &lt;em&gt;mirror&lt;/em&gt; your DB constraints in your ORM models, and don’t rely solely on ORM-level validation.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;for-the-data-architects-when-not-to-use-foreign-keys&quot;&gt;&lt;strong&gt;For The Data Architects: When NOT to Use Foreign Keys&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High-Volume Event Logs&lt;/strong&gt;: Can slow inserts; consider only if strong integrity is a must.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Denormalized Warehouses&lt;/strong&gt;: Use constraints during ETL, not at warehouse runtime.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Third-Party/Cross-DB Data&lt;/strong&gt;: Can’t enforce with foreign keys; use programmatic or scheduled validations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Microservices&lt;/strong&gt;: Sometimes, each service owns its data: cross-database FK constraints can break scaling and autonomy.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;checklist-production-ready-foreign-key-design&quot;&gt;&lt;strong&gt;Checklist: Production-Ready Foreign Key Design&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;All FKs indexed?&lt;/li&gt;
&lt;li&gt;Datatypes &lt;em&gt;exactly&lt;/em&gt; match?&lt;/li&gt;
&lt;li&gt;All cascade options reviewed and intentional?&lt;/li&gt;
&lt;li&gt;No accidental circular references?&lt;/li&gt;
&lt;li&gt;Soft deletes and TTL data not cascaded on delete?&lt;/li&gt;
&lt;li&gt;Batch loads/disables FKs as needed?&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;resources-for-further-mastery&quot;&gt;&lt;strong&gt;Resources for Further Mastery&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MySQL Foreign Key Docs:&lt;/strong&gt; &lt;em&gt;Deep-dive with syntax and real-world use cases.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Normalization and ER Diagrams:&lt;/strong&gt; Strengthen foundation in data modeling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Books:&lt;/strong&gt; “SQL Antipatterns” (Bill Karwin), “Designing Data-Intensive Applications” (Martin Kleppmann).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;In summary:&lt;/strong&gt; Foreign keys are your shield &lt;em&gt;and&lt;/em&gt; your sword—&lt;em&gt;they safeguard data integrity, enable powerful querying, and make your system maintainable at scale—but demand planning, discipline, and regular review&lt;/em&gt;.&lt;/p&gt;
</description>
<category>Data Engineering</category>
<guid isPermaLink="true">https://coderlegion.com/6029/foreign-keys-in-mysql-for-serious-system-design</guid>
<pubDate>Fri, 03 Oct 2025 21:33:56 +0000</pubDate>
</item>
<item>
<title>Deep Learning is Non Negotiable Now</title>
<link>https://coderlegion.com/4395/deep-learning-is-non-negotiable-now</link>
<description>&lt;p&gt;A dev shipped a feature using AI help. &lt;strong&gt;Clean code, no errors, delivered on time.&lt;/strong&gt; Looked perfect — until it crashed under load.&lt;/p&gt;
&lt;p&gt;Digging deeper, the issue was hidden inside a hashmap — an O(n²) performance bottleneck the AI had quietly introduced. But the real problem? He &lt;strong&gt;didn’t know how hashmaps work&lt;/strong&gt; well enough to spot it.&lt;/p&gt;
&lt;p&gt;Another story from Reddit — a guy merged some AI-generated tree traversal logic. Passed the basic tests, looked great. But in production, it &lt;strong&gt;broke on edge cases.&lt;/strong&gt; Took him days to find the bug… and when he did, he still didn’t fully understand why it broke.&lt;/p&gt;
&lt;p&gt;These aren’t rare cases anymore. I see &lt;strong&gt;this pattern constantly.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AI writes code fast, sure. But when things break — and they will — we realize we’ve outsourced the understanding too.&lt;/p&gt;
&lt;p&gt;This is why I personally make time to keep learning: &lt;strong&gt;through docs, books, deep-dive videos, or even just experimenting with my own bugs&lt;/strong&gt;. Constantly. If you like learning about programming concepts, data structures &amp;amp; algorithms, or System Design using real world problems, you can check &lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.com/@yogicode1&quot;&gt;my lists here.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The truth is, AI hasn’t made learning optional. It’s made it non-negotiable.&lt;br&gt;
Because when the autopilot fails, you need to know how to fly the damn plane.&lt;/p&gt;
</description>
<category>Full-stack Dev</category>
<guid isPermaLink="true">https://coderlegion.com/4395/deep-learning-is-non-negotiable-now</guid>
<pubDate>Sat, 02 Aug 2025 20:47:36 +0000</pubDate>
</item>
<item>
<title>Composition vs. Inheritance in PHP Frameworks</title>
<link>https://coderlegion.com/3997/composition-vs-inheritance-in-php-frameworks</link>
<description>&lt;p&gt;I have already published this article, but I believe it covers essential concepts that are language-independent. I chose PHP and its frameworks to compare &lt;strong&gt;inheritance and composition&lt;/strong&gt;, but the basic ideas can be found in many other languages and frameworks as well. It is essential for writing clean, maintainable code. Many developers dive into popular frameworks and use their powerful features without fully grasping the principles behind them. Yet, in our fast-paced and constantly evolving world, maintainability and reliability are crucial. In this post, I’ll briefly introduce both concepts and demonstrate how they are applied in well-known PHP frameworks like Slim or Laravel by exploring real code examples.&lt;/p&gt;
&lt;h3 id=&quot;understanding-inheritance&quot;&gt;Understanding Inheritance&lt;/h3&gt;
&lt;p&gt;Let’s begin by defining &lt;strong&gt;inheritance&lt;/strong&gt;. In the object-oriented world, a class represents a logical unit that models real-world concepts. When you create an instance of a class, you get an object. Objects have &lt;strong&gt;properties&lt;/strong&gt; (data) and &lt;strong&gt;methods&lt;/strong&gt; (behavior), and they can also relate to other objects.&lt;/p&gt;
&lt;p&gt;One such relationship is &lt;strong&gt;inheritance&lt;/strong&gt;, where a class (called a child or subclass) inherits properties and methods from another class (the parent or superclass). This allows the child class to reuse or override functionality defined in the parent, supporting extensibility and reducing code duplication.&lt;/p&gt;
&lt;p&gt;In PHP, inheritance is only possible between &lt;strong&gt;classes&lt;/strong&gt; and &lt;strong&gt;interfaces&lt;/strong&gt;. A class can inherit from only one other class—this is known as &lt;strong&gt;single inheritance&lt;/strong&gt;. While PHP doesn’t support multiple inheritance directly, it offers a feature called &lt;strong&gt;traits&lt;/strong&gt; to help simulate it. However, I’ll focus solely on classical inheritance here.&lt;/p&gt;
&lt;p&gt;Inheritance plays an important role in promoting the &lt;strong&gt;Don't Repeat Yourself (DRY)&lt;/strong&gt; principle, making your codebase more maintainable and easier to extend.&lt;/p&gt;
&lt;h3 id=&quot;what-is-composition-&quot;&gt;What Is Composition?&lt;/h3&gt;
&lt;p&gt;Now, let’s explore what &lt;strong&gt;composition&lt;/strong&gt; means. Think about the word itself—composition. What comes to mind? That’s right: creating something by putting smaller pieces together.&lt;/p&gt;
&lt;p&gt;In programming, composition is the practice of building a class by using instances of other classes. Instead of inheriting behavior from a parent class, you're assembling functionality from separate, focused components. Each of these components (or &quot;pieces&quot;) serves a clear, specific purpose, and together, they form a more complex system.&lt;/p&gt;
&lt;p&gt;You might wonder: Why is this so important? The answer lies in one of the biggest challenges in software development—&lt;strong&gt;maintainability.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In our fast-paced, ever-changing world, codebases need to be adaptable. Composition supports this by reducing tight coupling and making it easier to modify or replace individual parts without causing ripple effects elsewhere. It also enhances &lt;strong&gt;testability&lt;/strong&gt;. You can isolate individual components and write targeted tests for each one.&lt;/p&gt;
&lt;p&gt;And as we all know, a high-quality codebase is impossible without good test coverage. With composition, you gain confidence in your system’s behavior because every business use case can be verified through well-structured tests.&lt;/p&gt;
&lt;h3 id=&quot;slim-framework&quot;&gt;Slim Framework&lt;/h3&gt;
&lt;p&gt;Let’s take a closer look at how inheritance and composition are used in the &lt;strong&gt;Slim Framework&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A good example of inheritance is the Logger class under the &lt;em&gt;Slim&lt;/em&gt; namespace. It extends an abstract class called &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/php-fig/log/blob/master/src/AbstractLogger.php&quot;&gt;AbstractLogger&lt;/a&gt;, which in turn implements the LoggerInterface from the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.php-fig.org/psr/psr-3/&quot;&gt;PSR-3&lt;/a&gt; standard. Abstract classes in PHP cannot be instantiated directly, so this setup provides a reusable foundation for custom logging logic while adhering to a standardized interface.&lt;/p&gt;
&lt;p&gt;This design allows developers to plug in their own logger implementations or use the default one in a consistent and predictable way, all while staying compatible with other PSR-3-compliant tools.&lt;/p&gt;
&lt;p&gt;Another example of inheritance in Slim is in its HTTP-related exception classes, such as &lt;em&gt;HttpException, HttpSpecializedException, and HttpForbiddenException&lt;/em&gt;. These classes share common behavior through inheritance but typically don’t contain complex logic. Instead, they override specific methods, implement a standard interface, or provide simple property access via getters and setters. This keeps the exception hierarchy clear and focused while still taking advantage of inheritance for code reuse.&lt;/p&gt;
&lt;p&gt;Another interesting case is the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/slimphp/Slim/blob/4.x/Slim/Routing/FastRouteDispatcher.php&quot;&gt;FastRouteDispatcher&lt;/a&gt; class in Slim, which extends the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nikic/FastRoute/blob/master/src/Dispatcher/GroupCountBased.php&quot;&gt;GroupCountBased&lt;/a&gt; class from the FastRoute library. Although the GroupCountBased class is marked as @final in its docblock, it isn't technically marked with the final keyword in code. This annotation is a &lt;strong&gt;convention&lt;/strong&gt;, signaling that the class &lt;strong&gt;should not&lt;/strong&gt; be extended, even if PHP itself doesn’t enforce it.&lt;/p&gt;
&lt;p&gt;In such cases, one could argue that a more flexible design using &lt;strong&gt;composition&lt;/strong&gt; or a &lt;strong&gt;decorator pattern&lt;/strong&gt; might be preferable. However, the Slim team may have chosen inheritance here for pragmatic reasons—such as tight integration with FastRoute, performance, or compatibility with previous versions.&lt;/p&gt;
&lt;p&gt;This also serves as a practical example of &lt;strong&gt;polymorphism&lt;/strong&gt;. Both &lt;em&gt;GroupCountBased&lt;/em&gt; and Slim’s &lt;em&gt;FastRouteDispatcher&lt;/em&gt; extend from the same base and can respond to shared method calls in different ways. This allows Slim to plug into FastRoute's routing system while still customizing behavior as needed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=17339756739677482035&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;a-composition-example-in-the-slim-framework&quot;&gt;A Composition Example in the Slim Framework&lt;/h3&gt;
&lt;p&gt;Now let’s look at how composition is used in the Slim Framework. When a class uses other classes to fulfill its responsibilities, we refer to it as a &quot;has-a&quot; relationship—one of the clearest signs of composition.&lt;/p&gt;
&lt;p&gt;Slim handles these relationships through &lt;strong&gt;dependency injection (DI)&lt;/strong&gt;. Interestingly, Slim doesn't include a DI container of its own. Instead, it's designed to work with any &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.php-fig.org/psr/psr-11/&quot;&gt;PSR-11-compatible&lt;/a&gt; container, such as &lt;a rel=&quot;nofollow&quot; href=&quot;https://php-di.org/&quot;&gt;PHP-DI&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/silexphp/Pimple&quot;&gt;Pimple&lt;/a&gt;, L&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/laminas/laminas-servicemanager&quot;&gt;aminas ServiceManager&lt;/a&gt;, or the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/symfony/dependency-injection&quot;&gt;Symfony DependencyInjection&lt;/a&gt; component.&lt;/p&gt;
&lt;p&gt;This means Slim is built around a &lt;strong&gt;standard interface&lt;/strong&gt;, not a concrete implementation. That’s a powerful design decision. It decouples the framework from any specific container and allows developers to plug in the tool that best fits their needs.&lt;/p&gt;
&lt;p&gt;It’s a great example of flexible, future-proof design. Have you ever thought about structuring your own applications this way? If so, you’re well on your way to building maintainable and scalable software.&lt;/p&gt;
&lt;h3 id=&quot;laravel&quot;&gt;Laravel&lt;/h3&gt;
&lt;p&gt;In Laravel, inheritance plays a key role in the framework’s core architecture. A clear example is the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/laravel/framework/blob/12.x/src/Illuminate/Foundation/Application.php&quot;&gt;Application&lt;/a&gt; class, which extends the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/laravel/framework/blob/12.x/src/Illuminate/Container/Container.php&quot;&gt;Container&lt;/a&gt; class. This relationship allows Laravel’s application instance to inherit all the powerful features of the service container—such as dependency resolution and binding—while adding its own framework-specific logic.&lt;/p&gt;
&lt;p&gt;A strong example of &lt;strong&gt;interface inheritance&lt;/strong&gt; in Laravel is the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/laravel/framework/blob/12.x/src/Illuminate/Contracts/Container/Container.php&quot;&gt;ContainerContract&lt;/a&gt; interface. This contract extends the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.php-fig.org/psr/psr-11/&quot;&gt;PSR-11&lt;/a&gt; ContainerInterface, which defines the basic behavior of a dependency injection (DI) container. Laravel builds upon this standard by expanding the interface to meet the needs of a full-featured framework, turning it into the foundation of Laravel's entire DI system.&lt;/p&gt;
&lt;p&gt;This design reflects Laravel’s &lt;strong&gt;contract-based architecture&lt;/strong&gt;—a principle where components depend on &lt;strong&gt;interfaces&lt;/strong&gt;, not concrete implementations. The Container class implements the ContainerContract, and Laravel components rely on the contract, making the system more flexible and testable.&lt;/p&gt;
&lt;h3 id=&quot;composition-in-laravel&quot;&gt;Composition in Laravel&lt;/h3&gt;
&lt;p&gt;Laravel is full of examples that showcase the power of composition, which is used extensively throughout its components.&lt;/p&gt;
&lt;p&gt;Take the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/laravel/framework/blob/12.x/src/Illuminate/Auth/Passwords/PasswordBroker.php&quot;&gt;PasswordBroker&lt;/a&gt; class, for instance. Rather than extending another class, it uses composition to handle password reset functionality. Dependencies like the UserProvider and other related services are injected via the constructor, allowing the class to collaborate with other components without tightly coupling to them.&lt;/p&gt;
&lt;p&gt;This design allows Laravel to be &lt;strong&gt;flexible and testable&lt;/strong&gt;. For instance, you can easily &lt;strong&gt;swap out&lt;/strong&gt; the &lt;em&gt;UserProvider&lt;/em&gt;, mock dependencies for unit testing, or extend behavior without modifying existing classes.&lt;/p&gt;
&lt;p&gt;This is the true strength of composition—it promotes loose coupling and makes your codebase easier to maintain, extend, and reuse.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Understanding the differences between &lt;strong&gt;inheritance and composition&lt;/strong&gt;—and knowing when to apply each—is a key step toward writing clean, maintainable code. When used appropriately, these principles help developers build flexible, testable, and scalable applications. And that ultimately leads to more satisfied teams, happier customers, and a healthier codebase.&lt;/p&gt;
</description>
<category>php</category>
<guid isPermaLink="true">https://coderlegion.com/3997/composition-vs-inheritance-in-php-frameworks</guid>
<pubDate>Mon, 14 Jul 2025 17:27:50 +0000</pubDate>
</item>
<item>
<title>8 Most Important System Design Concepts You Should Know</title>
<link>https://coderlegion.com/3625/8-most-important-system-design-concepts-you-should-know</link>
<description>&lt;p&gt;Get a Free System Design PDF with 158 pages by subscribing to our weekly newsletter: &lt;a rel=&quot;nofollow&quot; href=&quot;https://bit.ly/bbg-social&quot;&gt;https://bit.ly/bbg-social&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Animation tools: Adobe Illustrator and After Effects.&lt;/p&gt;
&lt;p&gt;Checkout our bestselling System Design Interview books: &lt;br&gt;
Volume 1: &lt;a rel=&quot;nofollow&quot; href=&quot;https://amzn.to/3Ou7gkd&quot;&gt;https://amzn.to/3Ou7gkd&lt;/a&gt;&lt;br&gt;
Volume 2: &lt;a rel=&quot;nofollow&quot; href=&quot;https://amzn.to/3HqGozy&quot;&gt;https://amzn.to/3HqGozy&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The digital version of System Design Interview books: &lt;a rel=&quot;nofollow&quot; href=&quot;https://bit.ly/3mlDSk9&quot;&gt;https://bit.ly/3mlDSk9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ABOUT US: &lt;br&gt;
Covering topics and trends in large-scale system design, from the authors of the best-selling System Design Interview series.&lt;/p&gt;
</description>
<category>Sys Design &amp; Arch</category>
<guid isPermaLink="true">https://coderlegion.com/3625/8-most-important-system-design-concepts-you-should-know</guid>
<pubDate>Sun, 29 Jun 2025 08:22:17 +0000</pubDate>
</item>
<item>
<title>Understanding DRAM Internals: How Channels, Banks, and DRAM Access Patterns Impact Performance</title>
<link>https://coderlegion.com/3523/understanding-dram-internals-how-channels-banks-dram-access-patterns-impact-performance</link>
<description>&lt;p&gt;Modern software performance depends heavily on how efficiently memory is accessed, and not just on raw CPU speed. Sequential memory access tends to be much faster than random access, and understanding why requires a deeper look inside the memory subsystem, especially DRAM’s design.&lt;/p&gt;
&lt;p&gt;There are two critical factors that influence memory performance:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Access time per memory access&lt;/strong&gt; → how long it takes to fetch a single piece of data from memory (this is where DRAM latency plays a role).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The number of memory accesses&lt;/strong&gt; → how often your program needs to fetch data, which depends on your choice of data structures, algorithms, data organization, and access patterns. Programs that reuse data or access nearby memory locations require fewer fetches due to CPU caching.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;This article focuses solely on the first factor → DRAM access latency → and explains how DRAM is architected → and how different access patterns affect its performance&lt;/strong&gt;. By grasping these fundamentals, you’ll be better equipped to write performance-aware code.&lt;/p&gt;
&lt;p&gt;(We won’t cover CPU caches or virtual memory in this article — those will be discussed in follow-up articles to complete the full memory access picture.)&lt;/p&gt;
&lt;h3 id=&quot;anatomy-of-dram-components&quot;&gt;Anatomy of DRAM Components&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6sm80z6udiqqdqqqjoi6.png&quot; alt=&quot;DRAM Components&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Channels:&lt;/strong&gt;&lt;br&gt;
DRAM modules are divided into multiple &lt;strong&gt;channels&lt;/strong&gt;. Each channel operates independently and has its own dedicated data bus, allowing true parallel data transfers across channels to the Memory Controller.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Bus per Channel:&lt;/strong&gt;&lt;br&gt;
Within each channel, &lt;strong&gt;the data bus is shared and serialized&lt;/strong&gt;. This means only one data transfer can occur at a time from any bank in the channel to the memory controller, even if multiple banks are being accessed in parallel.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jhzk362vstu5axogr3c5.png&quot; alt=&quot;Memory Access from a Rank&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ranks:&lt;/strong&gt;&lt;br&gt;
A rank is a group of 8 DRAM chips that work together to move 64 bits of data at once over the 64-bit data bus. Each chip adds 8 bits, taken from the corresponding row and column inside its own corresponding memory bank (Bank 3 in the above diagram). This way, all chips access and send their part of the data at the same time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tr2p03iyp5uio70b82ss.png&quot; alt=&quot;Memory Access from Banks&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Banks:&lt;/strong&gt;&lt;br&gt;
Each rank has several banks, which are smaller sections of memory. Banks can work independently and in parallel with each other, but inside each bank, memory accesses are serialized.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rows and Columns:&lt;/strong&gt;&lt;br&gt;
Each bank is organized as a grid of &lt;strong&gt;rows and columns&lt;/strong&gt;. Every intersection of a row and column corresponds to a &lt;strong&gt;memory cell&lt;/strong&gt; holding 1 byte of data, which represents one memory address.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Row Buffer:&lt;/strong&gt;&lt;br&gt;
Each bank has a &lt;strong&gt;row buffer&lt;/strong&gt; that holds the currently active row’s data for faster access. Only &lt;strong&gt;one&lt;/strong&gt; row per bank can be active at a time. That is why memory accesses within a single bank are serialized.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;how-dram-access-works-internally&quot;&gt;How DRAM Access Works Internally&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/28t9xn57wjfze4e98rit.png&quot; alt=&quot;Actual memory access flow&quot;&gt;&lt;/p&gt;
&lt;p&gt;When the CPU requests data from memory, it sends a memory address to the Memory Controller. The Memory Controller then translates this memory address into a specific &lt;strong&gt;bank, row and column&lt;/strong&gt; that corresponds to that address.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Each bank in the memory can have only one row active (open) at a time. There are three possible cases when accessing the requested row:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Row Buffer Conflict (~75 nanoseconds total):&lt;/strong&gt; If a different row (let’s call it Row X’) is currently active in the bank, the memory controller must first wait a bit (around 30 nanoseconds), then close (precharge) that active row (which takes about 15 nanoseconds), and finally open (activate) the requested row X (which takes another 15 nanoseconds). This process takes the longest time overall.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Row Buffer Hit (~15 nanoseconds total):&lt;/strong&gt;&lt;br&gt;
If the requested row X is already active, the controller skips the closing and opening steps. This means the access is much faster since no extra delays are involved.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Row Buffer Miss (~30 nanoseconds total):&lt;/strong&gt;&lt;br&gt;
If no row is currently active in the bank, the memory controller simply activates the requested row X (taking about 15 nanoseconds).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After the correct row is active, the memory controller reads the requested column Y (which takes about 15 nanoseconds). Finally, the bank returns the requested data to the memory controller, which then sends it back to the CPU.&lt;/p&gt;
&lt;h3 id=&quot;dram-bottlenecks-and-their-impact-on-memory-access&quot;&gt;DRAM Bottlenecks and Their Impact on Memory Access&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Shared Data Bus Bottleneck:&lt;/strong&gt; As mentioned earlier, only one data transfer can happen at a time per channel, even if multiple banks are active. This bottleneck generally becomes significant during bursty or highly parallel traffic when many banks try to send data simultaneously over the shared data bus per channel.&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Latency impact:&lt;/strong&gt; Each data transfer takes about 15 nanoseconds (tCAS).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sequential access:&lt;/strong&gt; This bottleneck is typically less noticeable for bursty sequential memory accesses because modern CPUs use caches (L1, L2, L3), virtual memory page caching, and hardware prefetching. These mechanisms significantly reduce the number of DRAM accesses required and enable smoother, continuous data transfers over the shared bus.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Random access:&lt;/strong&gt; In bursty random access patterns, memory requests tend to target many banks unpredictably and in parallel often leading to bus contention, queuing, and delays when the bus becomes saturated. Moreover, caching and prefetching techniques don’t work well with random accesses because the requests lack predictable patterns, so the system can’t reduce the number of DRAM accesses effectively, making the bus bottleneck impact even more severe for bursty random access patterns.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bank Access Bottlenecks:&lt;/strong&gt;&lt;br&gt;
As noted earlier, banks operate in parallel, but within each bank, memory accesses occur sequentially. Additionally, only one row can be active in a bank at any given time. Accessing a different row requires the bank to first precharge (close) the currently active row before activating the new one → a process that incurs significant delay.&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Latency impact:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Row buffer hit (same row active): ~&lt;strong&gt;15 ns&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Row buffer miss or conflict (need precharge optionally + activate): ~ &lt;strong&gt;30–75 ns&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sequential access:&lt;/strong&gt; When memory accesses tend to stay within the same row of a bank for some time, the chances of hitting an already active row (row buffer hit) are high. This minimizes costly precharge and activate cycles, resulting in faster access.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Random access:&lt;/strong&gt; Memory requests spread unpredictably across different rows and banks, causing frequent row buffer misses and forcing the bank to precharge and activate rows repeatedly. This serialization significantly increases latency due to longer row switching delays.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;conclusion-comparing-sequential-and-random-memory-access&quot;&gt;Conclusion: Comparing Sequential and Random Memory Access&lt;/h3&gt;
&lt;p&gt;Sequential memory accesses are generally faster because they align well with hardware optimizations like CPU caches, virtual memory page caching, memory prefetching, even during burst transfers. These mechanisms reduce both the number of DRAM accesses and the time per access by taking advantage of predictable patterns and row buffer hits.&lt;/p&gt;
&lt;p&gt;On the other hand, random accesses often suffer from row buffer conflicts, serialized access within banks, and data bus contention, especially during bursty or highly parallel traffic. Caching and prefetching are less effective here, making random patterns appear slower.&lt;/p&gt;
&lt;p&gt;However, random access isn’t inherently bad → when combined with smart data structures and algorithms that reduce the total number of memory fetches (like B-trees or hash maps), even high-latency accesses can be masked, leading to efficient overall performance.&lt;/p&gt;
</description>
<category>Performance</category>
<guid isPermaLink="true">https://coderlegion.com/3523/understanding-dram-internals-how-channels-banks-dram-access-patterns-impact-performance</guid>
<pubDate>Fri, 27 Jun 2025 05:29:08 +0000</pubDate>
</item>
<item>
<title>Atomic Design methodology: deconstructing web design into core components for cohesive, scalable systems</title>
<link>https://coderlegion.com/3089/atomic-design-methodology-deconstructing-design-components-cohesive-scalable-systems</link>
<description>&lt;h5 id=&quot;in-this-article-learn-how-to-deconstruct-interfaces-into-core-components-creating-unified-scalable-systems-using-brad-frost-s-atomic-design-methodology-to-enhance-your-web-design-skills-effectively-&quot;&gt;In this article, learn how to deconstruct interfaces into core components, creating unified, scalable systems using Brad Frost's Atomic Design methodology to enhance your web design skills effectively!&lt;/h5&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1747259377190/ac629fac-1ba6-4a73-b082-e557f4b91a47.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;what-is-atomic-design-&quot;&gt;&lt;strong&gt;What is&lt;/strong&gt; Atomic Design?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;What never ceases to amaze me in tech is that when I discover something new, like a language, framework, or methodology, the people credited with its creation are still with us and working in the industry!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Brad Frost is one such individual who has significantly impacted the design community with his concept of Atomic Design. This methodology revolutionizes how we approach web design by breaking down interfaces into their fundamental building blocks, or &quot;atoms.&quot; Doing so allows designers and developers to create more cohesive and scalable design systems. Atomic Design simplifies the design process and ensures consistency across various platforms and devices. Brad's work continues to influence modern design practices, demonstrating the lasting impact of innovative thinkers in the tech industry.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;atoms&quot;&gt;Atoms&lt;/h4&gt;
&lt;p&gt;Atoms are the most basic building blocks in Atomic Design. They are the smallest user interface elements, such as buttons, labels, input fields, and icons. Think of them as the fundamental pieces that you can combine to create more complex components.&lt;/p&gt;
&lt;p&gt;In web design, atoms are like HTML tags—simple and standalone. Focusing on atoms ensures consistency and reusability across your design system. For example, a button atom can be styled once and used throughout your entire project, maintaining a uniform look and feel. Understanding atoms is crucial because they form the foundation for more complex components.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1747415378173/d62874a3-4f47-4aa3-bfe1-69c2bc42ac92.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;molecules&quot;&gt;Molecules&lt;/h4&gt;
&lt;p&gt;Molecules are groups of atoms bonded together, serving as the next step in building a user interface. They combine two or more atoms to form a more complex component that maintains a simple function. For example, a search form molecule might consist of an input field atom and a button atom.&lt;/p&gt;
&lt;p&gt;By grouping atoms into molecules, you create more functional and interactive components. Molecules help organize your design system by providing reusable patterns that can be used across different project parts. They are essential for building consistent and efficient interfaces, allowing you to manage and update multiple atoms simultaneously.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1747414978873/09fbe0f2-f343-4517-af2a-02bec8f051f5.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Above is an image of the header section of our Elfgorithm App, which consists of an SVG icon and a styled text element. This combination forms a molecule in the context of Atomic Design. The header is structured as follows:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; element is styled with padding and flexbox properties to align its child elements (the icon and text) horizontally.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The SVG icon, representing a gift, is styled with specific dimensions, colors, and rounded corners to visually stand out.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The text element is styled with bold, italic, and specific color properties, with a portion of the text having a different color and weight for emphasis.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;This cohesive unit serves a specific function, demonstrating how molecules in Atomic Design combine multiple atoms to create more complex and functional components.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;organisms&quot;&gt;Organisms&lt;/h4&gt;
&lt;p&gt;Organisms are more complex components in Atomic Design. They are composed of groups of molecules and/or atoms working together to form a distinct section of an interface. Organisms represent a more substantial part of the UI, such as a navigation bar, a product listing, or a user profile section. Organisms are crucial for creating structured and functional layouts, combining various smaller components to serve a specific purpose.&lt;/p&gt;
&lt;p&gt;Using organisms, you can build more dynamic and interactive parts of your application, ensuring that the design remains consistent and scalable. They also allow you to manage and update multiple molecules and atoms collectively, making it easier to maintain the overall design system.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1747415630376/44b803ad-bf76-4b46-a2ee-93abccceec74.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The image above illustrates an organism within the context of Atomic Design. This section, titled &quot;How It Works,&quot; combines multiple molecules into a cohesive unit that serves a specific purpose. Each step in the process (Create a Group, AI Matches &amp;amp; Suggests, Enjoy the Exchange) is a molecule, consisting of a number icon, a heading, and a descriptive paragraph.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The entire section, with its heading and grid layout, forms a distinct part of the user interface, explaining the process in a structured and functional manner. This combination of multiple molecules into a larger, interactive layout is characteristic of an organism, representing a more substantial part of the UI.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;templates&quot;&gt;Templates&lt;/h4&gt;
&lt;p&gt;Templates are a key part of Atomic Design. They represent page-level objects that place components into a layout and demonstrate the design's underlying content structure. They are essentially a combination of organisms, molecules, and atoms arranged to form a cohesive layout. Templates focus on the page's structure rather than the specific content, allowing you to see how different components fit together.&lt;/p&gt;
&lt;p&gt;Using templates ensures that your design system is flexible and adaptable, accommodating various content types while maintaining a consistent layout. This helps visualize the final product's appearance and function, providing a blueprint for building actual pages.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;pages&quot;&gt;Pages&lt;/h4&gt;
&lt;p&gt;Pages are the final stage in Atomic Design, where templates are filled with real content to create a fully functional and complete user interface. They represent specific instances of templates, showcasing how the design system looks and behaves with actual data.&lt;/p&gt;
&lt;p&gt;Pages are crucial for testing the effectiveness of your design system, as they allow you to see how all the components work together in a real-world scenario. By using pages, you can ensure that your design is both visually consistent and functional as well as user-friendly. They provide a comprehensive view of the final product, helping you identify any issues or improvements needed before deployment.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1747245377926/cd41ec22-b237-40b2-8663-7ccb5dc1e95f.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h6 id=&quot;-atomic-design-by-brad-frost-an-event-apart-austin-2015&quot;&gt;&lt;em&gt;“&lt;/em&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/W-h1FtNYim4&quot;&gt;&lt;em&gt;Atomic Design&lt;/em&gt;&lt;/a&gt;&lt;em&gt;” by Brad Frost—An Event Apart Austin 2015&lt;/em&gt;&lt;/h6&gt;
&lt;p&gt;&lt;strong&gt;Brad Frost introduces atomic design, a methodology that breaks down interfaces into their basic elements to create scalable and maintainable design systems.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;real-world-use-case&quot;&gt;&lt;strong&gt;Real-World Use Case&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;AI: Work smarter, not harder! Effectively using AI as a tool rather than a substitute for your work is an excellent method to maximize efficiency without increasing effort!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Our tech lead, Shashi Lo, understands the proper use of AI and allows us to use it while we build our project. However, it comes with a caveat: you must be able to understand and explain to him how the AI-generated code works! Fair enough.&lt;/p&gt;
&lt;p&gt;I was tasked with creating a Google Doc that organizes our forty components and counting, labeling each individual component as an Atom, Molecule, or Organism.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Since the main goal is to write documentation for each component, and we were waiting for this clarification before we began, I utilized AI to analyze each component and categorize them as Atoms, Molecules, or Organisms. This demonstrates the effectiveness of proper component naming conventions, as the AI successfully sorted them all accordingly.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1747257191905/00f32d6a-b2d2-4a9c-bbba-7dbbf014cdce.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;However, it was not enough just to have AI label each component; I needed to understand why each component was labeled as it was. So, I took the AI assistance further and asked it to write definitions for each component.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Using AI as a tool in this manner saved our team time on the mundane task of manually labeling each component as Atoms, Molecules, or Organisms. With the Google Doc completed, with all the labeled components and their definitions, we can quickly approve or disapprove each labeled component and update if necessary, allowing us to finish this task and start the components documentation!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1747254633137/4afd8218-7a29-469b-baa9-cf45c28a5452.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;about-us&quot;&gt;About Us&lt;/h4&gt;
&lt;h5 id=&quot;what-is-gridiron-survivor-&quot;&gt;What is Gridiron Survivor?&lt;/h5&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.htmlallthethings.com/blog-posts/bridging-the-skills-gap-empowering-junior-developers-through-apprenticeship-programs&quot;&gt;&lt;em&gt;Gridiron Survivor&lt;/em&gt;&lt;/a&gt; &lt;em&gt;is an apprenticeship program created by Shashi Lo, a Senior UX Engineer at Microsoft. It aims to provide developers entering the tech industry with vital work experience. The program focuses on practical training in project management, coding practices, and team collaboration, offering mentorship and skills essential for success in their initial tech roles.&lt;/em&gt;&lt;/p&gt;
&lt;h5 id=&quot;what-is-the-elfgorithm-app-&quot;&gt;What is the Elfgorithm App?&lt;/h5&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/LetsGetTechnical/elecretanta&quot;&gt;&lt;em&gt;Elfgorithm&lt;/em&gt;&lt;/a&gt; &lt;em&gt;is an AI-driven Secret Santa app set to launch in winter 2025. It streamlines gift exchanges by removing the guesswork from Secret Santa activities. The app manages gift-giving details and provides personalized gift suggestions, ensuring you find the perfect presents for everyone.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1747239447501/9fae8e38-b558-41d5-8f48-d6234d45dafe.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;gridiron-survivor-sponsors&quot;&gt;Gridiron Survivor Sponsors&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;A very special thanks to our sponsors!&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.gitkraken.com/&quot;&gt;GitKraken&lt;/a&gt;: &lt;em&gt;A popular Git client that provides a graphical interface to manage Git repositories. It is known for its user-friendly design and features that simplify version control, making it easier for developers to collaborate and manage their code.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.frontendmentor.io/&quot;&gt;Frontend Mentor&lt;/a&gt;: &lt;em&gt;An online platform that offers front-end coding challenges. It helps developers improve their skills by providing real-world projects to work on, along with a supportive community for feedback and learning.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt;: &lt;em&gt;A cloud platform for static sites and serverless functions. It is designed to optimize the workflow of developers by providing tools for building, deploying, and scaling modern web applications with ease. Vercel is known for its seamless integration with frameworks like Next.js.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;my-other-related-articles&quot;&gt;My other related articles&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://selftaughttxg.com/2025/05-25/software-versioning-a-developers-guide-to-semantic-and-github-releases/&quot;&gt;Software Versioning: A Developer's Guide to Semantic and GitHub Releases&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://selftaughttxg.com/2025/04-25/gridiron-survivor's-elfgorithm-introduction-and-team-installation/&quot;&gt;Gridiron Survivor's Elfgorithm: Introduction and Team Installation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.htmlallthethings.com/blog-posts/bridging-the-skills-gap-empowering-junior-developers-through-apprenticeship-programs&quot;&gt;Bridging the Skills Gap: Empowering Junior Developers Through Apprenticeship Programs&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://selftaughttxg.com/2022/02-22/ShashiLo/&quot;&gt;Shashi Lo: UI Design Fundamentals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1740168177634/1ff578a9-5d03-4800-9c0c-9092e4e60c4f.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;be-sure-to-listen-to-the-html-all-the-things-podcast-&quot;&gt;&lt;strong&gt;Be sure to listen to the HTML All The Things Podcast!&lt;/strong&gt;&lt;/h4&gt;
&lt;h5 id=&quot;i-also-write-articles-for-the-html-all-the-things-podcast-which-you-can-read-on-their-website-https-www-htmlallthethings-com-&quot;&gt;&lt;em&gt;I also write articles for the HTML All The Things Podcast, which you can read on their website:&lt;/em&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.htmlallthethings.com/&quot;&gt;&lt;em&gt;https://www.htmlallthethings.com/&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/h5&gt;
&lt;h5 id=&quot;be-sure-to-check-out-html-all-the-things-on-socials-&quot;&gt;&lt;strong&gt;Be sure to check out HTML All The Things on socials!&lt;/strong&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://twitter.com/htmleverything&quot;&gt;Twitter&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/company/html-all-the-things/&quot;&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.tiktok.com/@htmlallthethings&quot;&gt;TikTok&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.instagram.com/htmlallthethings/&quot;&gt;Instagram&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;affiliate-amp-discount-links-&quot;&gt;Affiliate &amp;amp; Discount Links!&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1740168473785/c85789c9-1594-4000-aa2f-43e842f9825f.jpeg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;With CodeMonkey, learning can be all fun and games!&lt;/strong&gt; CodeMonkey transforms education into an engaging experience, enabling children to evolve from tech consumers to creators. Use CodeMonkey's &lt;strong&gt;FREE trial&lt;/strong&gt; to unlock the incredible potential of young tech creators!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;With a structured learning path tailored for various age groups, kids progress from block coding to more advanced topics like data science and artificial intelligence, using languages such as CoffeeScript and Python. The platform includes features for parents and teachers to track progress, making integrating coding into home and classroom settings easy.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Through fun games, hands-on projects, and community interaction, CodeMonkey helps young learners build teamwork skills and receive recognition for their achievements. It fosters a love for coding and prepares children for future career opportunities in an ever-evolving tech landscape.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;To learn more about CodeMonkey, you can read my detailed&lt;/em&gt;&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://selftaughttxg.com/2025/02-25/inspiring-young-coders-how-codemonkey-turns-kids-into-tech-creators/&quot;&gt;&lt;strong&gt;&lt;em&gt;review article&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;&lt;em&gt;!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Affiliate Links:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://codemonkey.sjv.io/c/5987452/919057/12259&quot;&gt;Sign Up for Parents&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://codemonkey.sjv.io/c/5987452/919060/12259&quot;&gt;Sign Up for Teachers&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1740169191630/ac8c5360-d1c5-4c85-8504-18b9c755841e.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;advance-your-career-with-a-20-discount-on-scrimba-pro-using-this-affiliate-link-&quot;&gt;Advance your career with a 20% discount on Scrimba Pro using this &lt;a rel=&quot;nofollow&quot; href=&quot;https://scrimba.com/?via=MichaelLarocca&quot;&gt;affiliate link&lt;/a&gt;!&lt;/h4&gt;
&lt;p&gt;Become a hireable developer with Scrimba Pro! Discover a world of coding knowledge with full access to all courses, hands-on projects, and a vibrant community. You can &lt;a rel=&quot;nofollow&quot; href=&quot;https://selftaughttxg.com/2021/06-21/06-07-21/&quot;&gt;read my article&lt;/a&gt; to learn more about my exceptional experiences with Scrimba and how it helps many become confident, well-prepared web developers!&lt;/p&gt;
&lt;h6 id=&quot;important-this-discount-is-for-new-accounts-only-if-a-higher-discount-is-currently-available-it-will-be-applied-automatically-&quot;&gt;&lt;strong&gt;&lt;em&gt;Important:&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;This discount is for new accounts only. If a higher discount is currently available, it will be applied automatically.&lt;/em&gt;&lt;/h6&gt;
&lt;p&gt;&lt;strong&gt;How to Claim Your Discount:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Click &lt;a rel=&quot;nofollow&quot; href=&quot;https://scrimba.com/?via=MichaelLarocca&quot;&gt;the link&lt;/a&gt; to explore the new Scrimba 2.0.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a new account.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upgrade to Pro; the 20% discount will automatically apply.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h6 id=&quot;disclosure-this-article-contains-affiliate-links-i-will-earn-a-commission-from-any-purchases-made-through-these-links-at-no-extra-cost-to-you-your-support-helps-me-continue-creating-valuable-content-thank-you-&quot;&gt;&lt;strong&gt;&lt;em&gt;Disclosure:&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;This article contains affiliate links. I will earn a commission from any purchases made through these links at no extra cost to you. Your support helps me continue creating valuable content. Thank you!&lt;/em&gt;&lt;/h6&gt;
&lt;hr&gt;
&lt;h4 id=&quot;conclusion-2&quot;&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Brad Frost's atomic Design methodology offers a structured approach to building cohesive and scalable design systems. It starts with Atoms, the smallest building blocks, such as buttons and icons, which provide consistency and reusability across the design. Moving up, Molecules are formed by combining atoms to create more functional components, like a search form. Organisms take it further by grouping molecules and atoms into larger, functional interface sections, such as navigation bars.&lt;/p&gt;
&lt;p&gt;Templates then arrange these components into page-level structures, focusing on layout rather than specific content. Finally, Pages brings templates to life by filling them with real content, resulting in a complete and testable user interface.&lt;/p&gt;
&lt;p&gt;By following these principles, designers and developers can create efficient, consistent, and user-friendly interfaces that work seamlessly across various platforms!&lt;/p&gt;
&lt;hr&gt;
&lt;h6 id=&quot;do-you-now-feel-confident-applying-atomic-design-principles-to-your-web-projects-have-you-created-more-cohesive-and-scalable-design-systems-and-have-tips-to-share-please-share-the-article-and-comment&quot;&gt;&lt;em&gt;Do you now feel confident applying Atomic Design principles to your web projects? Have you created more cohesive and scalable design systems and have tips to share? Please share the article and comment&lt;/em&gt;&lt;/h6&gt;
&lt;hr&gt;
</description>
<category>Site Reliability Engineering</category>
<guid isPermaLink="true">https://coderlegion.com/3089/atomic-design-methodology-deconstructing-design-components-cohesive-scalable-systems</guid>
<pubDate>Mon, 19 May 2025 18:19:23 +0000</pubDate>
</item>
<item>
<title>The subtle art of waiting</title>
<link>https://coderlegion.com/2800/the-subtle-art-of-waiting</link>
<description>&lt;p&gt;Recently, while working on a workshop titled &lt;a rel=&quot;nofollow&quot; href=&quot;https://loftlabs-experiments.github.io/workshop-test-pr-k8s/&quot;&gt;Testing Your Pull Request on Kubernetes with GKE, and GitHub Actions&lt;/a&gt;, I faced twice the same issue: service A needs service B, but service A starts faster than service B, and the system fails. In this post, I want to describe the context of these issues and how I solved them both with the same tool.&lt;/p&gt;
&lt;h3 id=&quot;waiting-in-kubernetes&quot;&gt;Waiting in Kubernetes&lt;/h3&gt;
&lt;p&gt;It might sound strange to wait in Kubernetes. The self-healing nature of the Kubernetes platform is one of its biggest benefits. Let's consider two pods: a Python application and a PostgreSQL database.&lt;/p&gt;
&lt;p&gt;The application starts very fast and eagerly tries to establish a connection to the database.  Meanwhile, the database is initializing itself with the provided data; the connection fails. The pod ends up in the &lt;code&gt;Failed&lt;/code&gt; state.&lt;/p&gt;
&lt;p&gt;After a while, Kubernetes requests the application pod's state. Because it's failed, it terminates it and starts a new pod. At this point, two things can happen: the database pod isn't ready yet, and it's back to square one or it's ready, and the application finally connects.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=14558761510786390433&quot; alt=&quot;Kubernetes self-healing sequence diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;To speed up the process, Kubernetes offers &lt;a rel=&quot;nofollow&quot; href=&quot;https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-startup-probes&quot;&gt;startup probes&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;startupProbe:
  httpGet:
    path: /health
    port: 8080
  failureThreshold: 30
  periodSeconds: 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the above probe, Kubernetes waits for an initial ten seconds before requesting the pod's status. If it fails, it waits for another ten seconds. Rinse and repeat 30 times before it fails definitely.&lt;/p&gt;
&lt;p&gt;You may have noticed the HTTP &lt;code&gt;/healthz&lt;/code&gt; endpoint above. Kubernetes offers two exclusive &lt;a rel=&quot;nofollow&quot; href=&quot;https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#Probe&quot;&gt;Probe&lt;/a&gt; configuration settings: &lt;code&gt;httpGet&lt;/code&gt; or &lt;code&gt;exec&lt;/code&gt;. The former is suitable for web applications, while the latter is for other applications. It implies we need to know which kind of container the pod contains and how to check its status, provided it can. I'm no PostgreSQL expert, so I searched for a status check command. The &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/bitnami/charts/blob/main/bitnami/postgresql/templates/primary/statefulset.yaml#L417-L427&quot;&gt;Bitnami Helm Chart&lt;/a&gt; looks like the following when applied:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;startupProbe:
  exec:
    command:
      - /bin/sh
      - -c
      - -e
      - exec pg_isready -U $PG_USER -h $PG_HOST -p $PG_PORT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the above is a simplification, as it gladly ignores the database name and an SSL certificate.&lt;/p&gt;
&lt;p&gt;The startup probe speeds things up compared to the default situation if you configure it properly. You can set a long initial delay, and then shorter increments. Yet, the more diverse the containers, the harder it gets to configure, as you need to be an expert in each of the underlying containers.&lt;/p&gt;
&lt;p&gt;It would be beneficial to look for alternatives.&lt;/p&gt;
&lt;h3 id=&quot;wait4x&quot;&gt;Wait4x&lt;/h3&gt;
&lt;p&gt;Alternatives are tools whose focus is on waiting. A long time ago, I found the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/eficode/wait-for&quot;&gt;wait-for&lt;/a&gt; script for this. The idea is straightforward:&lt;/p&gt;
&lt;p&gt;&amp;gt;&lt;code&gt;./wait-for&lt;/code&gt; is a script designed to synchronize services like docker containers. It is &lt;code&gt;sh&lt;/code&gt; and &lt;code&gt;alpine&lt;/code&gt; compatible.&lt;/p&gt;
&lt;p&gt;Here's how to wait for an HTTP API:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sh -c './wait-for http://my.api/health -- echo &quot;The api is up! Let's use it&quot;'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It got the job done, but at the time, you had to copy the script and manually check for updates. I've checked, and the project now provides a regular container.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wait4x.dev/&quot;&gt;wait4x&lt;/a&gt; plays the same role, but is available as a versioned container and provides more services to wait for: HTTP, DNS, databases, and message queues. That's my current choice.&lt;/p&gt;
&lt;p&gt;Whatever tool you use, you can use it inside an &lt;a rel=&quot;nofollow&quot; href=&quot;https://kubernetes.io/docs/concepts/workloads/pods/init-containers/[init&quot;&gt;https://kubernetes.io/docs/concepts/workloads/pods/init-containers/[init&lt;/a&gt; container]:&lt;/p&gt;
&lt;p&gt;&amp;gt;A Pod can have multiple containers running apps within it, but it can also have one or more init containers, which are run before the app containers are started.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&amp;gt;Init containers are regular containers, except:&lt;/p&gt;
&lt;p&gt;&amp;gt;&lt;em&gt; Init containers always run to completion.&lt;br&gt;
&amp;gt;&lt;/em&gt; Each init container must complete successfully before the next one starts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Imagine the following &lt;code&gt;Pod&lt;/code&gt; that depends on a PostgreSQL &lt;code&gt;Deployment&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  labels:
    type: app
    app: recommandations
spec:
  containers:
    - name: recommandations
      image: recommandations:latest
      envFrom:
        - configMapRef:
            name: postgres-config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The application is Python and starts quite fast. It attempts to connect to the PostgreSQL database. Unfortunately, the database hasn't finished initializing, so the connection fails, and Kubernetes restarts the pod.&lt;/p&gt;
&lt;p&gt;We can fix it with an &lt;code&gt;initContainer&lt;/code&gt; and a waiting container:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  labels:
    type: app
    app: recommandations
spec:
  initContainers:
    - name: wait-for-postgres
      image: atkrad/wait4x:3.1
      command:
        - wait4x
        - postgresql
        - postgres://$(DATABASE_URL)?sslmode=disable
      envFrom:
        - configMapRef:
            name: postgres-config
  containers:
    - name: recommandations
      image: recommandations:latest
      envFrom:
        - configMapRef:
            name: postgres-config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above setup, the &lt;code&gt;initContainer&lt;/code&gt; doesn't stop until the database accepts connections. When it does, it terminates, and the &lt;code&gt;recommandations&lt;/code&gt; container can start. Kubernetes doesn't need to terminate the &lt;code&gt;Pod&lt;/code&gt; as in the previous setup! It entails fewer logs and potentially fewer alerts.&lt;/p&gt;
&lt;h3 id=&quot;when-waiting-becomes-mandatory&quot;&gt;When waiting becomes mandatory&lt;/h3&gt;
&lt;p&gt;The above is a slight improvement, but you can do without it. In other cases, waiting becomes mandatory. I experienced it recently when preparing for the workshop mentioned above. The scenario is the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The pipeline applies a manifest on the Kubernetes side&lt;/li&gt;
&lt;li&gt;In the next step, it runs the test&lt;/li&gt;
&lt;li&gt;As the test starts before the application is read, it fails.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We must wait until the backend is ready before we test. Let's use &lt;code&gt;wait4x&lt;/code&gt; to wait for the &lt;code&gt;Pod&lt;/code&gt; to accept requests before we launch the tests:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- name: Wait until the application has started
  uses: addnab/docker-run-action@v3                                       #1
  with:
    image: atkrad/wait4x:latest
    run: wait4x http ${{ env.BASE_URL }}/health --expect-status-code 200  #2
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;The GitHub Action allows running a container. I could have downloaded the Go binary instead.&lt;/li&gt;
&lt;li&gt;Wait until the &lt;code&gt;/health&lt;/code&gt; endpoint returns a &lt;code&gt;200&lt;/code&gt; response code.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;conclusion-3&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Kubernetes startup probes are a great way to avoid unnecessary restarts when you start services that depend on each other. The alternative is an external waiting tool configured in an &lt;code&gt;initContainer&lt;/code&gt;. &lt;code&gt;wait4x&lt;/code&gt; is a tool that can be used in other contexts. It's now part of my toolbelt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To go further:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/wait4x/wait4x#wait4x&quot;&gt;wait4x&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://vadosware.io/post/so-you-need-to-wait-for-some-kubernetes-resources/&quot;&gt;So you need to wait for some Kubernetes resources?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published at &lt;a rel=&quot;nofollow&quot; href=&quot;https://blog.frankel.ch/subtle-art-waiting/&quot;&gt;A Java Geek&lt;/a&gt; on April 20&lt;sup&gt;th&lt;/sup&gt;, 2025&lt;/em&gt;&lt;/p&gt;
</description>
<category>Java</category>
<guid isPermaLink="true">https://coderlegion.com/2800/the-subtle-art-of-waiting</guid>
<pubDate>Wed, 23 Apr 2025 21:40:24 +0000</pubDate>
</item>
<item>
<title>A problem statement for my next few articles on System Design and Cloud Computing on AWS</title>
<link>https://coderlegion.com/1765/a-problem-statement-for-my-next-few-articles-on-system-design-and-cloud-computing-on-aws</link>
<description>&lt;h2 id=&quot;design-and-build-a-scalable-ecommerce-platform-on-aws&quot;&gt;Design and Build a scalable Ecommerce Platform on AWS&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;This article describe the problem statement of the project we'll be looking at over the next series of articles. The goal is to build this system from scratch - from the frontend pages, backend apis, database integrations and Cloud infrastructure&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;problem-statement&quot;&gt;Problem statement&lt;/h3&gt;
&lt;p&gt;You're building an e-commerce platform for a small-to-medium enterprise (SME) selling handmade, eco-friendly products. The company expects seasonal traffic spikes (especially during sales like Earth Day, Black Friday), and they need the site to be highly available, scalable, and secure. They also plan to expand globally within the next year.&lt;br&gt;
The MVP (minimum viable product) must be ready to handle 5,000 daily users with the capability to scale to 50,000 during sales events.&lt;br&gt;
 The CEO loves &quot;serverless&quot; buzzwords, but they’re open to containers if needed.&lt;/p&gt;
&lt;h4 id=&quot;business-requirements-detailed-&quot;&gt;Business Requirements (Detailed)&lt;/h4&gt;
&lt;p&gt; &lt;strong&gt;1. Core Functional Requirements&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Customers must browse products, add to cart, checkout, and receive email confirmations.&lt;/li&gt;
&lt;li&gt;Admins need an internal dashboard to manage inventory, orders, and customer data.&lt;/li&gt;
&lt;li&gt;Orders should trigger payment processing (assume you’re using Stripe).&lt;/li&gt;
&lt;li&gt;Inventory must update in real time to avoid overselling.&lt;/li&gt;
&lt;li&gt;Support for user accounts, including password reset and MFA.&lt;/li&gt;
&lt;li&gt;All product images and static assets must load quickly worldwide.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. Non-Functional Requirements&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scalability: Must handle spikes in traffic without downtime.&lt;/li&gt;
&lt;li&gt;Availability: 99.9% uptime (no single points of failure).&lt;/li&gt;
&lt;li&gt;Security:&lt;ul&gt;
&lt;li&gt;PCI-DSS compliance for payment data (Stripe handles most of it, but your app must be secure).&lt;/li&gt;
&lt;li&gt;Customer data must be encrypted at rest and in transit.&lt;/li&gt;
&lt;li&gt;Protection from common web attacks (SQL injection, XSS, DDoS).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Performance: Page load time under 3 seconds globally.&lt;/li&gt;
&lt;li&gt;Observability: Full monitoring, logging, and alerting for failures.&lt;/li&gt;
&lt;li&gt;Cost Optimization: Minimal operational overhead; pay-for-use preferred.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. User Requirements&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Customer Portal: Browse catalog, search products, checkout process.&lt;/li&gt;
&lt;li&gt;Admin Portal: CRUD for products, view orders, manage customers.&lt;/li&gt;
&lt;li&gt;Authentication: Cognito (or something equivalent), with social login (Google, Facebook).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4. Data Requirements&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Product catalog (name, price, description, image, stock).&lt;/li&gt;
&lt;li&gt;Customer data (PII, shipping info, order history).&lt;/li&gt;
&lt;li&gt;Orders (customer, items, payment status, shipping status).&lt;/li&gt;
&lt;li&gt;Audit logs (who did what, and when).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;5. Operational Requirements&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CI/CD pipeline: Push to main → automatic build, test, deploy.&lt;/li&gt;
&lt;li&gt;Blue/Green or Canary deployments for releases.&lt;/li&gt;
&lt;li&gt;Disaster recovery: Backup and restore within 4 hours (RTO). Data loss no more than 15 minutes (RPO).&lt;/li&gt;
&lt;li&gt;Cost reporting: Regular cost monitoring and alerts for unusual spikes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;6. Compliance &amp;amp; Legal&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GDPR compliance (data deletion on request, explicit consent for data processing).&lt;/li&gt;
&lt;li&gt;PCI-DSS (payment handling done through Stripe, but secure the backend and database).&lt;/li&gt;
&lt;li&gt;Data residency: Store customer data in EU and US regions based on customer location.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;next-steps-&quot;&gt;Next steps?&lt;/h4&gt;
&lt;p&gt;The next article will talk about Core Functional Requirements and the microservices we will be building to ensure we have an MVP. &lt;/p&gt;
</description>
<category>Cloud Computing</category>
<guid isPermaLink="true">https://coderlegion.com/1765/a-problem-statement-for-my-next-few-articles-on-system-design-and-cloud-computing-on-aws</guid>
<pubDate>Wed, 02 Apr 2025 09:15:08 +0000</pubDate>
</item>
</channel>
</rss>