<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>a.j.djalali</title><link>https://alexdjalali.github.io/</link><description>a.j.djalali's personal blog</description><language>en</language><managingEditor>a.j.djalali</managingEditor><lastBuildDate>Fri, 03 Apr 2026 15:51:57 +0000</lastBuildDate><atom:link href="https://alexdjalali.github.io/index.xml" rel="self" type="application/rss+xml"/><image><url>https://alexdjalali.github.io/images/headshot.png</url><title>a.j.djalali</title><link>https://alexdjalali.github.io/</link></image><item><title>Professional Software Engineering for AI and Distributed Systems</title><link>https://alexdjalali.github.io/posts/professional-software-engineering-for-ai-and-distributed-systems/</link><guid isPermaLink="true">https://alexdjalali.github.io/posts/professional-software-engineering-for-ai-and-distributed-systems/</guid><pubDate>Sun, 01 Mar 2026 00:00:00 +0000</pubDate><dc:creator>a.j.djalali</dc:creator><description>A graduate course designed to bridge the gap between writing code and engineering production-grade distributed systems — with intentional technical debt, architecture defense, and AI as a force multiplier.</description><content:encoded><![CDATA[<p>Computer science programs teach programming. They teach data structures, algorithms, complexity analysis, and language syntax. Students graduate knowing how to invert a binary tree, implement a hash map, and reason about Big-O notation. These are valuable skills. They are also insufficient.</p>
<p>The gap between writing code and engineering production systems is enormous. It is the difference between solving a problem in isolation and operating inside a system that other people depend on, that runs at scale, that must be deployed without downtime, monitored in production, debugged at 2 AM, and evolved over years by teams of people who were not there when the original decisions were made. Most CS programs do not teach this. Most graduates discover it on the job, through expensive mistakes and patient mentors — if they are lucky enough to have mentors at all.</p>
<p>This is the gap that <em>Professional Software Engineering for AI and Distributed Systems</em> is designed to close. It is a graduate-level course I am developing at <a href="https://www.gatech.edu/" target="_blank" rel="noopener noreferrer">Georgia Tech</a> that treats software engineering as a discipline distinct from computer science — one that requires its own vocabulary, its own practices, and its own modes of thinking.</p>
<h2 id="the-philosophy">
  The Philosophy
  <a class="heading-link" href="#the-philosophy">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The course is built on a single principle:</p>
<blockquote>
<p>You play like you practice.</p>
</blockquote>
<p>If you practice with toy projects, unrealistic environments, and no consequences for bad decisions, you will play that way in production. If you practice with production-parity development environments, real CI/CD pipelines, and architecture decisions that must be defended in front of peers, you will carry those habits into your career.</p>
<p>This means <a href="https://www.docker.com/" target="_blank" rel="noopener noreferrer">Docker</a> and <a href="https://containers.dev/" target="_blank" rel="noopener noreferrer">devcontainers</a> from day one — not as a topic in week twelve, but as the environment in which all coursework happens. It means <a href="https://12factor.net/dev-prod-parity" target="_blank" rel="noopener noreferrer">dev/prod parity</a> as a foundational constraint, not an aspiration. It means secrets management and configuration discipline before students write their first feature, because in production, misconfigured environments cause more outages than buggy code.</p>
<p>The course is organized around six pillars: engineering foundations, codebase and architecture mastery, distributed systems and production realism, operational excellence, AI-augmented engineering, and leadership and organizational impact. Each pillar spans multiple weeks, and each week builds on what came before.</p>
<h2 id="orion-a-deliberately-broken-system">
  Orion: A Deliberately Broken System
  <a class="heading-link" href="#orion-a-deliberately-broken-system">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Students do not start from scratch. They inherit a system called Orion — a Python-based distributed AI platform with a <a href="https://fastapi.tiangolo.com/" target="_blank" rel="noopener noreferrer">FastAPI</a> service layer, an async worker service, <a href="https://www.postgresql.org/" target="_blank" rel="noopener noreferrer">PostgreSQL</a>, <a href="https://redis.io/" target="_blank" rel="noopener noreferrer">Redis</a>, a message queue, an AI integration layer, and a Docker-based development environment. It functions. It also has problems.</p>
<p>The CI pipeline is broken. The codebase contains intentional technical debt — tightly coupled components, missing type annotations, inconsistent error handling, implicit dependencies between services, and no observability. There are no <a href="https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions" target="_blank" rel="noopener noreferrer">Architecture Decision Records</a>. There is no documentation explaining why things are the way they are.</p>
<p>This is deliberate. Starting with a clean codebase teaches students how to build. Starting with a messy one teaches them how to <em>engineer</em> — how to navigate unfamiliar systems, diagnose structural problems, introduce discipline incrementally, and make things better without breaking what already works. Every professional engineer inherits more code than they write. Orion reflects that reality.</p>
<p>Over the semester, students collaboratively evolve Orion into a production-ready platform. They refactor the architecture, introduce ADRs, add static typing, implement <a href="https://trunkbaseddevelopment.com/" target="_blank" rel="noopener noreferrer">trunk-based development</a>, build CI/CD pipelines, add observability, design safe database migrations, optimize performance and cost, secure the system against real threats, and ultimately defend their architectural decisions in a formal review.</p>
<h2 id="engineering-foundations">
  Engineering Foundations
  <a class="heading-link" href="#engineering-foundations">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The first three weeks establish the mindset and the mechanics.</p>
<p><strong>Week 1</strong> draws the line between a programmer and an engineer. A programmer writes code that works. An engineer owns a system — understands its failure modes, considers its operational characteristics, makes decisions under constraint, and accepts responsibility for the consequences. This distinction is not philosophical. It determines whether you ship a feature and walk away, or ship a feature and monitor its impact on latency, error rates, and downstream services. The week covers ownership, systems thinking, and the ethics of AI in engineering — because students will increasingly work alongside AI systems, and they need a framework for when to trust the output and when to question it.</p>
<p><strong>Week 2</strong> builds the development environment. Students configure Docker-based environments with development containers, enforce environment parity between development and production, and learn secrets management. The goal is that by the end of week two, every student can run the entire Orion stack locally with a single command, and the environment they run locally is structurally identical to the one that runs in CI. I have <a href="/blog/posts/my-development-environment/" >written previously</a> about the importance of investing in your development environment — this week puts that philosophy into practice.</p>
<p><strong>Week 3</strong> introduces trunk-based development. Students learn to work with short-lived branches, use CI as an enforcement mechanism, implement <a href="https://martinfowler.com/articles/feature-toggles.html" target="_blank" rel="noopener noreferrer">feature flags</a> for incomplete work, and practice rigorous code review. The emphasis is on small, frequent merges rather than long-lived feature branches — because in production, integration risk grows with branch age, and merge conflicts are a symptom of process failure, not code complexity.</p>
<h2 id="codebase-and-architecture-mastery">
  Codebase and Architecture Mastery
  <a class="heading-link" href="#codebase-and-architecture-mastery">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Weeks four and five teach students how to read and restructure large codebases.</p>
<p><strong>Week 4</strong> focuses on navigation. Static typing becomes the primary tool — not as an academic exercise, but as a means of understanding code you did not write. When a function signature tells you it accepts a <code>TranscriptQueryParams</code> and returns a <code>list[Transcript]</code>, you know what it does without reading the implementation. Students learn to identify modular boundaries, detect <a href="https://refactoring.guru/refactoring/smells" target="_blank" rel="noopener noreferrer">code smells</a>, and use type annotations as both documentation and a safety net for refactoring.</p>
<p><strong>Week 5</strong> introduces <a href="https://en.wikipedia.org/wiki/Dependency_inversion_principle" target="_blank" rel="noopener noreferrer">dependency inversion</a> and <a href="https://en.wikipedia.org/wiki/Composition_over_inheritance" target="_blank" rel="noopener noreferrer">composition over inheritance</a> — the principles that separate maintainable architectures from brittle ones. Students refactor legacy code in Orion to follow clean layered architecture: interfaces defined before implementations, dependency direction enforced through physical directory boundaries, cross-cutting concerns handled through decorator composition. I have written about this architecture in detail in my post on <a href="/blog/posts/the-ideal-repository-structure/" >the ideal repository structure</a> and in the companion post on <a href="/blog/posts/decorator-pattern-observability-resilience/" >the decorator pattern for observability and resilience</a>.</p>
<h2 id="testing-and-delivery">
  Testing and Delivery
  <a class="heading-link" href="#testing-and-delivery">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Weeks six and seven address how code gets validated and shipped.</p>
<p><strong>Week 6</strong> covers testing in distributed systems — a fundamentally different challenge from testing a monolithic application. Unit tests verify individual components. Integration tests verify interactions between services. <a href="https://pact.io/" target="_blank" rel="noopener noreferrer">Contract tests</a> verify that service boundaries honor their agreements. Students learn to design for idempotency — ensuring that retried operations produce the same result — and to simulate failure conditions: network partitions, slow databases, unavailable downstream services. The question is not &ldquo;does this work when everything is healthy?&rdquo; but &ldquo;does this degrade gracefully when something fails?&rdquo;</p>
<p><strong>Week 7</strong> introduces <a href="https://martinfowler.com/articles/continuousIntegration.html" target="_blank" rel="noopener noreferrer">CI/CD</a> and release engineering. Students build multi-stage pipelines that enforce quality gates — formatting, linting, type checking, tests — on every commit. They learn to produce reproducible builds, design rollback strategies, and treat the deployment pipeline as code that is versioned, tested, and reviewed with the same rigor as application code.</p>
<h2 id="distributed-systems-and-data-safety">
  Distributed Systems and Data Safety
  <a class="heading-link" href="#distributed-systems-and-data-safety">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Weeks eight and nine introduce the theory and practice of building systems that span multiple processes, machines, and failure domains.</p>
<p><strong>Week 8</strong> covers distributed systems fundamentals: consistency models, the trade-offs between availability and partition tolerance, retry strategies with exponential backoff and jitter, and <a href="https://martinfowler.com/articles/201701-event-driven.html" target="_blank" rel="noopener noreferrer">event-driven architecture</a>. The emphasis is practical — students implement these patterns in Orion, not in a textbook. They experience what happens when a message is delivered twice, when a consumer crashes mid-processing, when a network timeout causes a cascade of retries that overwhelms a downstream service.</p>
<p><strong>Week 9</strong> addresses schema evolution and data safety. Databases change. APIs change. The challenge is making those changes without downtime and without breaking existing clients. Students learn zero-downtime migration patterns — expand and contract, parallel writes, backfill strategies — and implement versioned APIs that allow old clients to coexist with new ones. In production, the most dangerous deploys are not code changes. They are data changes.</p>
<h2 id="operational-excellence">
  Operational Excellence
  <a class="heading-link" href="#operational-excellence">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Weeks ten and eleven shift focus from building systems to running them.</p>
<p><strong>Week 10</strong> introduces observability and <a href="https://sre.google/sre-book/table-of-contents/" target="_blank" rel="noopener noreferrer">SRE</a> thinking. Students instrument Orion with structured logging, metrics, and distributed tracing. They define <a href="https://sre.google/sre-book/service-level-objectives/" target="_blank" rel="noopener noreferrer">service level objectives</a> — not as abstract percentages, but as concrete commitments about latency, error rates, and availability that drive engineering decisions. They practice incident retrospectives: blameless postmortems that focus on systemic causes rather than individual mistakes. The goal is to shift from reactive firefighting to proactive reliability engineering.</p>
<p><strong>Week 11</strong> covers performance and cost engineering. Students learn to profile applications systematically — CPU profiling, memory profiling, query analysis — rather than guessing at bottlenecks. They run load tests against Orion and discover where the system breaks under pressure. And because Orion integrates AI services, they learn about token economics: the cost of LLM inference at scale, strategies for reducing token consumption without degrading quality, and the trade-offs between model capability and operational cost. In production, performance is not just about speed. It is about sustainability.</p>
<h2 id="security-and-ai">
  Security and AI
  <a class="heading-link" href="#security-and-ai">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Weeks twelve and thirteen address the two domains where the stakes are highest.</p>
<p><strong>Week 12</strong> covers security through the lens of production systems. Students perform <a href="https://owasp.org/www-community/Threat_Modeling" target="_blank" rel="noopener noreferrer">threat modeling</a> against Orion — identifying attack surfaces, classifying threats, and prioritizing mitigations. They learn <a href="https://slsa.dev/" target="_blank" rel="noopener noreferrer">supply chain security</a>: dependency auditing, SBOM generation, pinned dependencies, and the risks of transitive trust. And because Orion includes an AI integration layer, they study <a href="https://owasp.org/www-project-top-10-for-large-language-model-applications/" target="_blank" rel="noopener noreferrer">prompt injection</a> — how adversarial inputs can manipulate LLM behavior, and what defenses look like in practice.</p>
<p><strong>Week 13</strong> focuses on AI-augmented engineering. Not AI as a toy, and not AI as a replacement for engineering judgment — AI as a professional tool that requires the same discipline as any other tool in the stack. Students use LLMs for large-scale refactors, AI-assisted test generation, and code review augmentation. They learn to evaluate hallucinations systematically: how to detect when a model&rsquo;s output is confidently wrong, how to design workflows that catch errors before they ship, and how to build the kind of engineering system I described in <a href="/blog/posts/anatomy-of-an-ai-engineering-system/" >a previous post</a> — where AI operates within explicit constraints rather than unconstrained improvisation. The line between <a href="/blog/posts/how-i-learned-to-love-the-bomb/" >leverage and liability</a> is drawn by the discipline of the person holding the tool.</p>
<h2 id="organization-and-defense">
  Organization and Defense
  <a class="heading-link" href="#organization-and-defense">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The final two weeks connect engineering to organizational reality.</p>
<p><strong>Week 14</strong> covers the human side of software engineering: writing stories that developers can actually implement, estimating work without false precision, managing technical debt as a portfolio rather than a backlog item, and navigating the tension between shipping fast and shipping well. These are the skills that determine whether an engineer leads or follows — and they are almost never taught in CS programs.</p>
<p><strong>Week 15</strong> is the architecture defense. Each team presents the evolution of their Orion system — the decisions they made, the alternatives they considered, the trade-offs they accepted, and the evidence that supports their choices. ADRs, architecture diagrams, observability dashboards, test coverage reports, CI/CD pipeline runs, and live demonstrations. This is not a final exam. It is a simulation of the architectural review process that exists at every serious engineering organization — the moment where you must explain not just <em>what</em> you built, but <em>why</em>.</p>
<h2 id="what-students-leave-with">
  What Students Leave With
  <a class="heading-link" href="#what-students-leave-with">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>By the end of this course, students will be able to operate inside distributed AI systems, architect responsibly, refactor safely at scale, ship production-ready software, lead technical discussions, and use AI as a force multiplier — not a crutch.</p>
<p>More importantly, they will have practiced these skills under conditions that mirror the real world. Not in isolation. Not with toy data. Not with unlimited time and no constraints. They will have inherited a broken system, fixed it collaboratively, defended their decisions publicly, and shipped something that works. That experience is transferable to the first day of a professional engineering role in a way that a passing grade on a data structures final is not.</p>
<h2 id="course-reading">
  Course Reading
  <a class="heading-link" href="#course-reading">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The following books, articles, and resources form the reading list for the course, organized by module.</p>
<h3 id="engineering-foundations-weeks-13">
  Engineering Foundations (Weeks 1–3)
  <a class="heading-link" href="#engineering-foundations-weeks-13">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<ul>
<li><a href="https://abseil.io/resources/swe-book" target="_blank" rel="noopener noreferrer">Software Engineering at Google</a> by Titus Winters, Tom Manshreck, and Hyrum Wright — Chapters 1–3 on what distinguishes software engineering from programming, and how Google thinks about code over time. Free to read online.</li>
<li><a href="https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/" target="_blank" rel="noopener noreferrer">The Pragmatic Programmer</a> by David Thomas and Andrew Hunt — Chapters 1–2 on pragmatic philosophy: ownership, responsibility, and the &ldquo;broken windows&rdquo; theory of software decay.</li>
<li><a href="https://web.stanford.edu/~ouster/cgi-bin/book.php" target="_blank" rel="noopener noreferrer">A Philosophy of Software Design</a> by John Ousterhout — Chapters 1–4 on complexity as the central problem, and the distinction between tactical and strategic programming.</li>
<li><a href="https://12factor.net/" target="_blank" rel="noopener noreferrer">The Twelve-Factor App</a> by Adam Wiggins — all twelve factors, with emphasis on <a href="https://12factor.net/dev-prod-parity" target="_blank" rel="noopener noreferrer">Factor X: Dev/Prod Parity</a> and <a href="https://12factor.net/config" target="_blank" rel="noopener noreferrer">Factor III: Config</a>.</li>
<li><a href="https://trunkbaseddevelopment.com/" target="_blank" rel="noopener noreferrer">Trunk Based Development</a> by Paul Hammant — the complete site, covering short-lived branches, branch by abstraction, and feature flags.</li>
<li><a href="https://martinfowler.com/articles/feature-toggles.html" target="_blank" rel="noopener noreferrer">Feature Toggles</a> by Pete Hodgson (Martin Fowler&rsquo;s site) — taxonomy of toggle types, lifecycle management, and the operational burden of flags.</li>
<li><a href="https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions" target="_blank" rel="noopener noreferrer">Documenting Architecture Decisions</a> by Michael Nygard — the original blog post that introduced lightweight ADRs.</li>
</ul>
<h3 id="codebase-and-architecture-mastery-weeks-45">
  Codebase and Architecture Mastery (Weeks 4–5)
  <a class="heading-link" href="#codebase-and-architecture-mastery-weeks-45">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<ul>
<li><a href="https://refactoring.com/" target="_blank" rel="noopener noreferrer">Refactoring: Improving the Design of Existing Code</a> by Martin Fowler — the catalog of refactorings and the chapter on code smells. The companion site <a href="https://refactoring.guru/refactoring/smells" target="_blank" rel="noopener noreferrer">Refactoring Guru</a> provides visual explanations of each smell.</li>
<li><strong>Clean Architecture</strong> by Robert C. Martin — Part V on architecture, with emphasis on the dependency rule, the stable abstractions principle, and the separation of policy from detail.</li>
<li><a href="https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/" target="_blank" rel="noopener noreferrer">The Pragmatic Programmer</a> — Chapter 5 on decoupling: the Law of Demeter, metaprogramming, and reducing coupling between components.</li>
<li><a href="/posts/the-ideal-repository-structure/" >The Ideal Repository Structure</a> — how strict layer boundaries prevent architectural decay, with Go and Python examples of interface-first, layered monorepo design.</li>
<li><a href="/posts/decorator-pattern-observability-resilience/" >The Decorator Pattern for Observability and Resilience</a> — composing cross-cutting concerns through decorator stacks rather than scattering them through business logic.</li>
</ul>
<h3 id="testing-and-delivery-weeks-67">
  Testing and Delivery (Weeks 6–7)
  <a class="heading-link" href="#testing-and-delivery-weeks-67">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<ul>
<li><a href="https://www.oreilly.com/library/view/working-effectively-with/0131177052/" target="_blank" rel="noopener noreferrer">Working Effectively with Legacy Code</a> by Michael Feathers — the definitive guide to introducing tests into codebases that were not designed for testability. Chapters on seam identification and breaking dependencies.</li>
<li><a href="https://martinfowler.com/articles/practical-test-pyramid.html" target="_blank" rel="noopener noreferrer">The Practical Test Pyramid</a> by Ham Vocke (Martin Fowler&rsquo;s site) — a modern restatement of the test pyramid with concrete examples of unit, integration, and end-to-end tests.</li>
<li><a href="https://docs.pact.io/" target="_blank" rel="noopener noreferrer">Pact Documentation</a> — consumer-driven contract testing for microservices. How to verify that service boundaries honor their agreements without running full integration environments.</li>
<li><a href="https://continuousdelivery.com/" target="_blank" rel="noopener noreferrer">Continuous Delivery</a> by Jez Humble and David Farley — the foundational text on deployment pipelines, reproducible builds, and the practice of keeping software in a releasable state at all times.</li>
<li><a href="https://martinfowler.com/articles/continuousIntegration.html" target="_blank" rel="noopener noreferrer">Continuous Integration</a> by Martin Fowler — the principles behind CI as a practice, not just a tool.</li>
</ul>
<h3 id="distributed-systems-and-data-safety-weeks-89">
  Distributed Systems and Data Safety (Weeks 8–9)
  <a class="heading-link" href="#distributed-systems-and-data-safety-weeks-89">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<ul>
<li><a href="https://dataintensiveapplications.com/" target="_blank" rel="noopener noreferrer">Designing Data-Intensive Applications</a> by Martin Kleppmann — Part II on distributed data: replication, partitioning, consistency, and consensus. The single most important textbook for understanding distributed systems in practice.</li>
<li><a href="https://martinfowler.com/articles/201701-event-driven.html" target="_blank" rel="noopener noreferrer">What do you mean by &ldquo;Event-Driven&rdquo;?</a> by Martin Fowler — disambiguating event notification, event-carried state transfer, event sourcing, and CQRS.</li>
<li><a href="https://jepsen.io/consistency" target="_blank" rel="noopener noreferrer">Jepsen: Consistency Models</a> by Kyle Kingsbury — a visual guide to the hierarchy of consistency models, from linearizability to eventual consistency.</li>
<li><a href="https://queue.acm.org/detail.cfm?id=3025012" target="_blank" rel="noopener noreferrer">Life beyond Distributed Transactions</a> by Pat Helland — how to build correct applications without relying on distributed transactions, using idempotency and natural keys.</li>
<li><a href="https://pragprog.com/titles/mnee2/release-it-second-edition/" target="_blank" rel="noopener noreferrer">Release It!</a> by Michael Nygard — stability patterns (circuit breakers, bulkheads, timeouts) and anti-patterns (cascading failures, blocked threads, self-denial attacks). Essential reading for building systems that survive production.</li>
</ul>
<h3 id="operational-excellence-weeks-1011">
  Operational Excellence (Weeks 10–11)
  <a class="heading-link" href="#operational-excellence-weeks-1011">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<ul>
<li><a href="https://sre.google/sre-book/table-of-contents/" target="_blank" rel="noopener noreferrer">Site Reliability Engineering</a> edited by Betsy Beyer, Chris Jones, Jennifer Petoff, and Niall Richard Murphy — Chapters on <a href="https://sre.google/sre-book/service-level-objectives/" target="_blank" rel="noopener noreferrer">service level objectives</a>, <a href="https://sre.google/sre-book/monitoring-distributed-systems/" target="_blank" rel="noopener noreferrer">monitoring distributed systems</a>, and <a href="https://sre.google/sre-book/postmortem-culture/" target="_blank" rel="noopener noreferrer">postmortem culture</a>. Free to read online.</li>
<li><a href="https://sre.google/workbook/table-of-contents/" target="_blank" rel="noopener noreferrer">The Site Reliability Workbook</a> — the practical companion to the SRE book, with worked examples of SLO implementation, alerting strategies, and incident response. Free to read online.</li>
<li><a href="https://www.oreilly.com/library/view/observability-engineering/9781492076438/" target="_blank" rel="noopener noreferrer">Observability Engineering</a> by Charity Majors, Liz Fong-Jones, and George Miranda — the distinction between monitoring and observability, structured events over metrics, and high-cardinality debugging in distributed systems.</li>
<li><a href="https://how.complexsystems.fail/" target="_blank" rel="noopener noreferrer">How Complex Systems Fail</a> by Richard I. Cook — a short, essential paper on why complex systems fail, how failure is evaluated after the fact, and why hindsight bias distorts incident analysis. Required reading before the first retrospective exercise.</li>
</ul>
<h3 id="security-and-ai-weeks-1213">
  Security and AI (Weeks 12–13)
  <a class="heading-link" href="#security-and-ai-weeks-1213">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<ul>
<li><a href="https://owasp.org/www-project-top-10/" target="_blank" rel="noopener noreferrer">OWASP Top 10</a> — the standard reference for web application security risks: injection, broken authentication, sensitive data exposure, and the rest. Students should be able to identify each risk category in Orion&rsquo;s codebase.</li>
<li><a href="https://owasp.org/www-project-top-10-for-large-language-model-applications/" target="_blank" rel="noopener noreferrer">OWASP Top 10 for Large Language Model Applications</a> — prompt injection, insecure output handling, training data poisoning, and other LLM-specific attack vectors. The AI-era companion to the classic Top 10.</li>
<li><a href="https://slsa.dev/spec/v1.0/" target="_blank" rel="noopener noreferrer">SLSA Framework</a> (Supply-chain Levels for Software Artifacts) — a security framework for achieving increasing levels of supply chain integrity, from basic provenance to hermetic builds.</li>
<li><strong>Threat Modeling: Designing for Security</strong> by Adam Shostack — the STRIDE methodology, attack trees, and practical approaches to identifying threats before they become vulnerabilities.</li>
<li><a href="/posts/anatomy-of-an-ai-engineering-system/" >Anatomy of an AI Engineering System</a> — how composable skills, quality gates, and model tiers turn AI tools into a disciplined engineering workflow rather than unconstrained generation.</li>
<li><a href="/posts/how-i-learned-to-love-the-bomb/" >How I Learned to Love the Bomb</a> — the shift from AI skepticism to building an engineering system around it, and why the leverage is real only if you bring the discipline with you.</li>
</ul>
<h3 id="organization-and-leadership-weeks-1415">
  Organization and Leadership (Weeks 14–15)
  <a class="heading-link" href="#organization-and-leadership-weeks-1415">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<ul>
<li><a href="https://itrevolution.com/product/accelerate/" target="_blank" rel="noopener noreferrer">Accelerate</a> by Nicole Forsgren, Jez Humble, and Gene Kim — the research behind the four key metrics (deployment frequency, lead time, change failure rate, mean time to recovery) and how they predict organizational performance.</li>
<li><a href="https://teamtopologies.com/book" target="_blank" rel="noopener noreferrer">Team Topologies</a> by Matthew Skelton and Manuel Pais — how team structure shapes software architecture (and vice versa), the four fundamental team types, and the three interaction modes.</li>
<li><strong>The Mythical Man-Month</strong> by Frederick P. Brooks Jr. — essays on why adding people to a late project makes it later, the distinction between essential and accidental complexity, and the surgical team model. Published in 1975, still correct.</li>
<li><a href="https://abseil.io/resources/swe-book" target="_blank" rel="noopener noreferrer">Software Engineering at Google</a> — Chapters 9–10 on code review culture and documentation, and Chapter 22 on large-scale changes.</li>
</ul>
<h2 id="the-guiding-principle">
  The Guiding Principle
  <a class="heading-link" href="#the-guiding-principle">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Professional software engineering is not about writing code.</p>
<p>It is about owning systems, making decisions under constraint, and shipping reliable software in real organizations.</p>
<p>Everything in this course — the production-parity environments, the deliberately broken codebase, the architecture defenses, the AI integration — serves that principle. The tools and technologies will change. The languages will evolve. The AI capabilities will grow. But the discipline of engineering — the habit of making explicit decisions, enforcing quality, and accepting responsibility for what you ship — that endures.</p>
]]></content:encoded><category>software-engineering</category><category>education</category><category>distributed-systems</category><category>ai</category><category>architecture</category></item><item><title>Anatomy of an AI Engineering System</title><link>https://alexdjalali.github.io/posts/anatomy-of-an-ai-engineering-system/</link><guid isPermaLink="true">https://alexdjalali.github.io/posts/anatomy-of-an-ai-engineering-system/</guid><pubDate>Thu, 26 Feb 2026 00:00:00 +0000</pubDate><dc:creator>a.j.djalali</dc:creator><description>Seventeen slash commands, seven templates, and three model tiers — how a set of short, composable skills turns Claude Code into a full software development lifecycle.</description><content:encoded><![CDATA[<p>In <a href="/posts/how-i-learned-to-love-the-bomb/" >a previous post</a> I described the shift from AI skepticism to building an engineering system around it. I mentioned standards files, quality gates, and the idea that unconstrained AI produces mediocre code while constrained AI produces code that looks like yours. What I didn&rsquo;t show was the implementation — the actual mechanism that turns a blank terminal into a full development lifecycle.</p>
<p>This post opens it up. Seventeen slash commands. Seven templates. Three model tiers. Each skill is short, composable, and single-purpose. Together they form a pipeline that mirrors how software has always been built — just faster.</p>
<h2 id="the-lifecycle-as-a-directed-graph">
  The Lifecycle as a Directed Graph
  <a class="heading-link" href="#the-lifecycle-as-a-directed-graph">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Traditional software development follows a predictable arc: understand the problem, make architectural decisions, break the work into deliverable pieces, plan each piece, implement with tests, verify, commit, review, ship. Every methodology from waterfall to agile to shape-up is a variation on this sequence. The differences are in the feedback loops and the batch sizes, not in the fundamental ordering.</p>
<p>My skills encode this arc as a directed graph of slash commands:</p>
<pre tabindex="0"><code>/adr → /arch → /rfp → /spec → /spec-plan → /spec-implement → /spec-verify → /github
</code></pre><p>Each node in the graph is a standalone skill that reads the outputs of its predecessors and produces artifacts for its successors. <code>/adr</code> writes an Architecture Decision Record. <code>/arch</code> generates Mermaid diagrams from codebase analysis. <code>/rfp</code> decomposes an epic into independently testable stories. <code>/spec</code> dispatches to planning, implementation, or verification based on where the plan currently sits. <code>/github</code> handles branching, committing, PRs, and merges.</p>
<p>The key property is that every skill terminates by offering to route you to the next one. Here&rsquo;s how <code>/adr</code> does it — the entire routing logic at the end of the skill:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>Use AskUserQuestion:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  question: &#34;What&#39;s the next step for this decision?&#34;
</span></span><span style="display:flex;"><span>  header: &#34;Pipeline&#34;
</span></span><span style="display:flex;"><span>  options:
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">-</span> &#34;/arch — Diagram affected architecture&#34;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">-</span> &#34;/rfp — Decompose into stories&#34;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">-</span> &#34;/spec — Implement directly&#34;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">-</span> &#34;Done — Record only&#34;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Based on the user&#39;s choice, invoke the corresponding skill:
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">-</span> /arch: Skill(skill=&#39;arch&#39;, args=&#39;&lt;scope derived from ADR context&gt;&#39;)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">-</span> /rfp: Skill(skill=&#39;rfp&#39;, args=&#39;&lt;epic description from ADR&gt;&#39;)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">-</span> /spec: Skill(skill=&#39;spec&#39;, args=&#39;&lt;task description from ADR&gt;&#39;)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">-</span> Done: End the workflow
</span></span></code></pre></div><p>The developer chooses the next step. The system suggests; it doesn&rsquo;t dictate. You can enter the graph at any point — jump straight to <code>/spec</code> for a small feature, start at <code>/adr</code> for a structural change — and the downstream skills will work regardless of how you arrived.</p>
<h2 id="each-skill-is-short">
  Each Skill is Short
  <a class="heading-link" href="#each-skill-is-short">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The longest skill in the system is <code>/spec-plan</code> at roughly 120 lines of markdown. Most are under 60. <code>/preflight</code> — which runs formatting, linting, type checking, and tests across three languages — is about 40 lines. <code>/tdd</code> — which implements the full red-green-refactor cycle with property-based testing — is around 50.</p>
<p>This is deliberate. A skill isn&rsquo;t a manual. It&rsquo;s a set of constraints, a sequence of steps, and a definition of done. The model already knows how to write Go or run pytest. What it doesn&rsquo;t know is <em>your</em> conventions: which linter, which formatter, which test markers, which directory structure, what constitutes &ldquo;done&rdquo; in your specific codebase. That&rsquo;s what the skill provides — the delta between general capability and your particular expectations.</p>
<p>Here&rsquo;s the entire rules section of <code>/debug</code> — four lines that define its behavioral contract:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#75715e">## Rules
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">-</span> NEVER change code before understanding the root cause
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">-</span> NEVER fix a bug without a test that reproduces it
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">-</span> NEVER apply a fix that you can&#39;t explain
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">-</span> If the fix is complex, consider whether the underlying design needs an ADR
</span></span></code></pre></div><p>And here are the rules from <code>/tdd</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#75715e">## Rules
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">-</span> NEVER write implementation before a failing test
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">-</span> NEVER write more test than needed for the current step
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">-</span> NEVER skip the refactor step (even if the code &#34;looks fine&#34;)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">-</span> If a test passes on first run, the test is probably wrong — investigate
</span></span></code></pre></div><p>That&rsquo;s the whole enforcement mechanism. No lengthy prose about why TDD matters. No motivational preamble. The model already knows what TDD is. The skill tells it <em>your</em> non-negotiable constraints and nothing more.</p>
<p>Short skills are also debuggable skills. When something goes wrong — and it will — you want to be able to read the entire skill in thirty seconds, identify which step produced the bad output, and adjust. A 500-line prompt is an untestable monolith. A 50-line prompt is a function.</p>
<h2 id="templates-as-shared-memory">
  Templates as Shared Memory
  <a class="heading-link" href="#templates-as-shared-memory">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Skills don&rsquo;t work in isolation. They share structure through a set of seven templates that live in <code>~/.claude/templates/</code>:</p>
<ul>
<li><strong>adr.md</strong> — Architecture Decision Record with alternatives table, consequences, and quality checklist</li>
<li><strong>epic.md</strong> — Epic specification with stories table and risk matrix</li>
<li><strong>story.md</strong> — User story with acceptance criteria and coding patterns checklist</li>
<li><strong>plan.md</strong> — Implementation plan with task breakdown, feature inventory, and progress tracking</li>
<li><strong>commit.md</strong> — Conventional commit format with scope and type conventions</li>
<li><strong>pr.md</strong> — Pull request with summary, changes by area, and test plan</li>
<li><strong>repo.md</strong> — Monorepo scaffold with layered architecture and directory conventions</li>
</ul>
<p>When <code>/adr</code> creates an ADR, it stamps out the <code>adr.md</code> template and fills in the blanks. When <code>/rfp</code> decomposes an epic into stories, each story uses the <code>story.md</code> template. When <code>/spec-plan</code> produces an implementation plan, it follows <code>plan.md</code> — complete with a status header (<code>PENDING</code>, <code>COMPLETE</code>, <code>VERIFIED</code>), an approved flag, and an iteration counter that increments when verification fails and the plan loops back to implementation.</p>
<p>The templates serve as shared memory across sessions. A plan file written by <code>/spec-plan</code> on Monday is picked up by <code>/spec-implement</code> on Tuesday and <code>/spec-verify</code> on Wednesday. The status header is the handoff mechanism. <code>/spec</code> reads it and dispatches to the right phase automatically. No human has to remember where they left off. The artifact carries the state.</p>
<p>This also means skills don&rsquo;t need to be long, because the structural decisions live in templates. The skill says <em>what to do</em>. The template says <em>what the output looks like</em>. Separation of concerns, applied to prompts.</p>
<h2 id="the-spec-pipeline-as-a-filesystem">
  The Spec Pipeline as a Filesystem
  <a class="heading-link" href="#the-spec-pipeline-as-a-filesystem">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Every skill reads from and writes to a specific path. The directory structure <em>is</em> the workflow engine:</p>
<pre tabindex="0"><code>docs/
├── adr/                    # Architecture Decision Records
└── spec/
    ├── arch/               # Mermaid architecture diagrams
    ├── epics/              # Epic specifications
    ├── stories/            # Implementation stories
    └── plans/              # Implementation plans
</code></pre><p><code>/repo</code> scaffolds this structure on day one — it&rsquo;s part of the standard monorepo template. From that point forward, every skill in the pipeline knows exactly where to find its inputs and where to deposit its outputs. <code>/adr</code> writes numbered files to <code>docs/adr/</code>. <code>/arch</code> writes diagrams to <code>docs/spec/arch/</code>. <code>/rfp</code> reads an epic from <code>docs/spec/epics/</code> and creates stories in <code>docs/spec/stories/</code>. <code>/spec-plan</code> creates plans in <code>docs/spec/plans/</code>. No configuration. No database. Just files in agreed-upon locations.</p>
<p>The naming conventions are part of the contract. Epics are <code>epic-NN-&lt;slug&gt;.md</code>. Stories use <code>&lt;epic&gt;.&lt;story&gt;-&lt;slug&gt;.md</code> — so story 5.3 of epic 5 lands at <code>docs/spec/stories/5.3-repository-layer.md</code>. Plans are <code>YYYY-MM-DD-&lt;slug&gt;.md</code>. The skills use these conventions to discover related artifacts automatically. When <code>/rfp</code> decomposes epic 5, it creates stories 5.1 through 5.N and updates the epic&rsquo;s stories table with relative links:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#75715e">## Stories
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>| #   | Story            | Status | File                                   |
</span></span><span style="display:flex;"><span>| --- | ---------------- | ------ | -------------------------------------- |
</span></span><span style="display:flex;"><span>| 5.1 | Core interfaces  | Todo   | [<span style="color:#f92672">5.1-core-interfaces.md</span>](<span style="color:#a6e22e">../stories/5.1-core-interfaces.md</span>) |
</span></span><span style="display:flex;"><span>| 5.2 | Repository layer | Todo   | [<span style="color:#f92672">5.2-repository-layer.md</span>](<span style="color:#a6e22e">../stories/5.2-repository-layer.md</span>) |
</span></span><span style="display:flex;"><span>| 5.3 | Service layer    | Todo   | [<span style="color:#f92672">5.3-service-layer.md</span>](<span style="color:#a6e22e">../stories/5.3-service-layer.md</span>) |
</span></span></code></pre></div><p>Status tracking happens in-file, not by moving files between directories. Each story has a <code>Status</code> field that progresses from <code>Todo</code> to <code>In Progress</code> to <code>Complete</code>. When <code>/spec-verify</code> finishes verifying a story&rsquo;s plan, it updates the story&rsquo;s status field and the epic&rsquo;s table in one pass. <code>/rfp status</code> scans the entire tree to generate a progress report:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>| # | Epic                       | Total | Done | Remaining | Progress         |
</span></span><span style="display:flex;"><span>|---|----------------------------|-------|------|-----------|------------------|
</span></span><span style="display:flex;"><span>| 1 | Repository Restructure     | 9     | 9    | 0         | ████████░░ 100%  |
</span></span><span style="display:flex;"><span>| 2 | Foundation Fault Tolerance | 8     | 5    | 3         | █████░░░░░  63%  |
</span></span></code></pre></div><p>The filesystem approach has a practical advantage over any database or project management tool: it&rsquo;s version-controlled. Every status change, every story creation, every plan iteration is a git commit. You can <code>git log docs/spec/stories/5.3-service-layer.md</code> and see the full history of a story from creation through implementation to completion. You can <code>git diff</code> a plan between iteration 1 and iteration 3 to see exactly what the verification phase changed.</p>
<p>Every template includes a section for references — links to ADRs, architecture diagrams, epic specs, and story files. When <code>/spec-plan</code> creates a plan, it cross-references the ADR that motivated the work and the story that scoped it. When <code>/github</code> generates a PR description, it links back to the plan, the story, and the ADR. This creates a traceable chain from decision to implementation to commit. Six months later, when someone asks &ldquo;why did we restructure the event pipeline?&rdquo;, the PR links to the plan, the plan links to the story, the story links to the epic, and the epic links to the ADR that documents the decision, the alternatives considered, and the trade-offs accepted.</p>
<p>The AI doesn&rsquo;t maintain this chain because it has good memory. It maintains it because the templates have a <code>References</code> section and the skills say <em>fill it in</em>. Structure beats intelligence.</p>
<h2 id="model-tiers">
  Model Tiers
  <a class="heading-link" href="#model-tiers">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Not every skill needs the same model. Reviewing an architecture decision requires different capability than running a linter. The skills encode this by specifying which model tier to use:</p>
<p>The encoding is a single YAML frontmatter line at the top of each skill file. <code>/adr</code> opens with:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span><span style="color:#f92672">model</span>: <span style="color:#ae81ff">opus</span>
</span></span><span style="display:flex;"><span>---
</span></span></code></pre></div><p>And <code>/preflight</code> opens with:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span><span style="color:#f92672">model</span>: <span style="color:#ae81ff">sonnet</span>
</span></span><span style="display:flex;"><span>---
</span></span></code></pre></div><p>That&rsquo;s the entire model selection mechanism. <strong>Opus</strong> handles the work that requires judgment: <code>/adr</code>, <code>/arch</code>, <code>/spec-plan</code>, <code>/spec-verify</code>, <code>/review</code>, <code>/repo</code>, and <code>/debug</code>. These skills involve reading large codebases, making design decisions, evaluating trade-offs, and catching subtle errors. They benefit from deeper reasoning and broader context comprehension.</p>
<p><strong>Sonnet</strong> handles the work that requires execution: <code>/spec</code> (dispatcher), <code>/spec-implement</code>, <code>/tdd</code>, <code>/github</code>, <code>/preflight</code>, <code>/learn</code>, <code>/patterns</code>, <code>/sync</code>, and <code>/vault</code>. These skills follow well-defined steps — run the tests, format the code, create the branch, commit with conventional format. The instructions are precise enough that a faster, cheaper model executes them reliably.</p>
<p>The division mirrors how engineering teams work. Senior architects make the structural decisions. Everyone follows the same process for committing code and running CI. You don&rsquo;t need your most expensive resource reviewing whether <code>ruff format</code> passed.</p>
<p>In practice, this means a typical <code>/spec</code> session starts with Opus for planning — exploring the codebase, designing the approach, breaking work into tasks — then drops to Sonnet for implementation, where it runs TDD cycles on each task, and finally escalates back to Opus for verification, where it launches review agents and checks for regressions. The model tier follows the cognitive demand of each phase.</p>
<h2 id="the-spec-workflow">
  The Spec Workflow
  <a class="heading-link" href="#the-spec-workflow">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The centerpiece is <code>/spec</code>, a three-phase workflow that manages the full implementation cycle:</p>
<p><strong>Phase 1 — Plan</strong> (<code>/spec-plan</code>, Opus): Explores the codebase. Identifies affected files, similar features, existing patterns. Designs 3–12 implementation tasks, each independently testable. Launches two verification agents in parallel — one checks alignment with requirements, one challenges assumptions and finds failure modes. The plan doesn&rsquo;t proceed until the developer approves it.</p>
<p><strong>Phase 2 — Implement</strong> (<code>/spec-implement</code>, Sonnet): Takes the approved plan and executes each task sequentially using <code>/tdd</code> for the red-green-refactor cycle. Updates the plan&rsquo;s checkboxes after every completed task. Commits per-task when working in a git worktree. No sub-agents — everything runs in the main context to preserve continuity.</p>
<p><strong>Phase 3 — Verify</strong> (<code>/spec-verify</code>, Opus): Launches two review agents — one for process compliance, one for code quality. Runs the full test suite, checks coverage, validates file lengths, traces call chains upstream and downstream. Then builds, deploys to a test environment, executes the actual program, and runs end-to-end tests. If anything fails, it adds fix tasks to the plan, resets the status, and loops back to Phase 2.</p>
<p>The loop is the important part. The <code>/spec</code> skill encodes it as a status machine:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>PENDING (Not Approved) → spec-plan    → User approves
</span></span><span style="display:flex;"><span>PENDING (Approved)     → spec-implement → All tasks done → COMPLETE
</span></span><span style="display:flex;"><span>COMPLETE               → spec-verify   → All checks pass → VERIFIED
</span></span></code></pre></div><p>And the feedback path:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>spec-verify finds issues → Status: PENDING → spec-implement fixes →
</span></span><span style="display:flex;"><span>  COMPLETE → spec-verify → ... → VERIFIED
</span></span></code></pre></div><p>Verification doesn&rsquo;t just report problems — it feeds them back into implementation as new tasks. The plan file&rsquo;s iteration counter tracks how many times this has happened. Most features verify on the first pass. Complex migrations sometimes take two or three iterations. The system handles both without human intervention beyond the initial approval.</p>
<h2 id="quality-gates-are-non-negotiable">
  Quality Gates are Non-Negotiable
  <a class="heading-link" href="#quality-gates-are-non-negotiable">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p><code>/preflight</code> runs before every commit. Four gates, in order:</p>
<ol>
<li><strong>Format</strong> — <code>ruff format</code> for Python, <code>gofumpt</code> for Go, <code>prettier</code> for TypeScript. Auto-fixes in place.</li>
<li><strong>Lint</strong> — <code>ruff check --fix</code>, <code>golangci-lint run</code>, <code>eslint --fix</code>. Reports remaining errors.</li>
<li><strong>Type check</strong> — <code>basedpyright</code>, <code>go vet</code>, <code>tsc --noEmit</code>. No auto-fix available. You fix it or you don&rsquo;t commit.</li>
<li><strong>Tests</strong> — <code>pytest</code>, <code>go test</code>, <code>vitest</code>. Changed modules must have passing tests.</li>
</ol>
<p>This isn&rsquo;t a suggestion. <code>/github commit</code> delegates to <code>/preflight</code> before staging anything. If gate 3 or 4 fails, the commit doesn&rsquo;t happen. The AI fixes the issue and tries again. The same bar applies whether the code was written by hand, by Sonnet, or by Opus.</p>
<h2 id="the-feedback-loop">
  The Feedback Loop
  <a class="heading-link" href="#the-feedback-loop">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The pipeline skills handle the forward path — from decision to shipped code. A second set of skills handles the feedback path: learning from what happened and improving the system itself.</p>
<p><code>/debug</code> applies the scientific method to bugs. Reproduce with a failing test, isolate the root cause through bisection, diagnose, fix via <code>/tdd</code>, verify no regressions. It prevents the most common AI failure mode: randomly changing code until the symptom disappears without understanding the cause.</p>
<p><code>/learn</code> watches for non-obvious discoveries during a session and extracts them into reusable skill files. The skill defines its own trigger conditions:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>| Trigger                      | Example                                              |
</span></span><span style="display:flex;"><span>| ---------------------------- | ---------------------------------------------------- |
</span></span><span style="display:flex;"><span>| <span style="font-weight:bold">**Non-obvious debugging**</span>    | Spent 10+ minutes investigating; solution wasn&#39;t in docs |
</span></span><span style="display:flex;"><span>| <span style="font-weight:bold">**Misleading errors**</span>        | Error message pointed wrong direction                |
</span></span><span style="display:flex;"><span>| <span style="font-weight:bold">**Workarounds**</span>              | Found limitation and creative solution               |
</span></span><span style="display:flex;"><span>| <span style="font-weight:bold">**Tool integration**</span>         | Figured out how to use tool/API in undocumented way  |
</span></span><span style="display:flex;"><span>| <span style="font-weight:bold">**Trial-and-error**</span>          | Tried multiple approaches before finding what worked |
</span></span><span style="display:flex;"><span>| <span style="font-weight:bold">**Repeatable workflow**</span>      | Multi-step task that will recur; worth standardizing |
</span></span></code></pre></div><p>Each extracted skill lands at <code>.claude/skills/</code>. The next session benefits from what this session learned. Over weeks, the system accumulates institutional knowledge that would otherwise evaporate when the context window resets.</p>
<p><code>/patterns</code> audits the codebase for DRY violations, anti-patterns, coupling problems, and complexity hotspots. It&rsquo;s the skill I run when something feels wrong but I can&rsquo;t point to a specific bug. It produces a severity-ranked report and offers to fix what it finds.</p>
<p><code>/sync</code> keeps the documentation layer current. It reads the codebase, compares what it finds against existing rules and skills, updates what&rsquo;s stale, and creates new rules for patterns it discovers but can&rsquo;t find documented anywhere. The system doesn&rsquo;t just execute — it maintains itself.</p>
<p>These aren&rsquo;t part of the main pipeline. They&rsquo;re the maintenance loop that keeps the pipeline honest. Without them, the skills would calcify — correct when written, increasingly irrelevant as the codebase evolves.</p>
<h2 id="why-this-works">
  Why This Works
  <a class="heading-link" href="#why-this-works">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The system works for the same reason any good engineering process works: it separates decisions from execution, makes the decisions explicit, and automates the execution.</p>
<p>The developer decides the architecture (via <code>/adr</code>), approves the plan (via <code>/spec-plan</code>), and reviews the output (via <code>/spec-verify</code>). The model executes the mechanical translation — the 400 lines of well-structured, well-tested implementation that used to take an afternoon. The templates ensure structural consistency. The model tiers allocate capability where it matters. The quality gates prevent regression.</p>
<p>None of this requires the model to be brilliant. It requires the model to follow instructions reliably, and it requires the instructions to be precise. Seventeen short skills, each doing one thing, chained together through file-based artifacts and status headers. That&rsquo;s the whole system.</p>
<p>The source of leverage isn&rsquo;t the AI. It&rsquo;s the constraints you put around it.</p>
]]></content:encoded><category>ai</category><category>software-engineering</category><category>developer-tools</category><category>claude</category><category>architecture</category></item><item><title>The Ideal Repository Structure</title><link>https://alexdjalali.github.io/posts/the-ideal-repository-structure/</link><guid isPermaLink="true">https://alexdjalali.github.io/posts/the-ideal-repository-structure/</guid><pubDate>Tue, 24 Feb 2026 00:00:00 +0000</pubDate><dc:creator>a.j.djalali</dc:creator><description>How strict layer boundaries prevent architectural decay — a deep-dive into interface-first, layered monorepo design with Go and Python examples.</description><content:encoded><![CDATA[<p>Most repositories start with good intentions. The first hundred commits are organized, components are small and focused, and dependency flow is obvious. By commit five thousand, the codebase resembles a tangled graph — services depend on repositories that depend back on services, infrastructure concerns leak into business logic, circular imports require elaborate workarounds, and every change carries the risk of breaking something distant and unrelated. The structure has rotted because there was no mechanism to enforce boundaries.</p>
<p>Flat structures invite disorder. When everything lives in a single directory, or when layering exists only as a naming convention without enforcement, developers default to expedient choices rather than architecturally sound ones. A service needs database access, so it imports the repository directly. The repository needs to emit an event, so it imports the event bus. The event bus needs to log, so it imports the logger. Three months later, every component depends on every other component, and the dependency graph is cyclic. Refactoring becomes impossible because changes ripple unpredictably across the codebase.</p>
<p>Layers are the antidote. Physical directory boundaries enforce dependency direction — code can depend downward on lower layers, never upward or sideways. A strict layer model transforms implicit architectural conventions into explicit compilation failures. When a repository attempts to import from a service, the compiler rejects it. When a workflow tries to access a controller, the import path does not resolve. The structure becomes self-enforcing, and architectural decay stops being a gradual slide toward chaos.</p>
<h2 id="the-inspiration-ardanlabs-service">
  The Inspiration: Ardanlabs Service
  <a class="heading-link" href="#the-inspiration-ardanlabs-service">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The foundation for this structure came from Bill Kennedy&rsquo;s <a href="https://github.com/ardanlabs/service" target="_blank" rel="noopener noreferrer">ardanlabs/service</a> template. Kennedy&rsquo;s approach introduced a three-layer model for Go services that made dependency direction visible through directory structure:</p>
<pre tabindex="0"><code>service/
├── foundation/    # Utilities, logging, metrics, database drivers
├── business/      # Domain logic, business rules, data access
└── app/           # HTTP handlers, service composition, main()
</code></pre><p>The breakthrough insight was physical enforcement. The <code>foundation/</code> layer contains infrastructure primitives — loggers, metric emitters, database clients, cache adapters — with zero dependencies on anything above it. The <code>business/</code> layer implements domain logic and data models, depending only on <code>foundation/</code>. The <code>app/</code> layer wires everything together and exposes HTTP endpoints, depending on both <code>foundation/</code> and <code>business/</code> but owned by neither.</p>
<p>This structure prevents common pathologies. A database repository cannot import an HTTP handler because handler code lives in <code>app/</code>, and <code>business/</code> is forbidden from importing <code>app/</code>. A logger cannot depend on business logic because <code>foundation/</code> has no upward dependencies. Every import violation is a compile-time error, not a runtime surprise discovered during a production incident.</p>
<p>Kennedy&rsquo;s model established two principles that remain central: <strong>dependency direction flows downward</strong>, and <strong>layers are directories, not comments</strong>. The first principle ensures that lower layers are reusable and testable in isolation — the logger does not need a database to function, and the business logic does not need an HTTP server to execute. The second principle makes architecture visible and enforceable — static analysis tools can verify that no <code>business/</code> file imports from <code>app/</code>, and code review can catch violations before they merge.</p>
<p>The ardanlabs model was designed for small to medium Go services. As the pattern scaled to larger polyglot monorepos with dozens of microservices and shared libraries, the three-layer division became insufficient. Business logic stratified into distinct responsibilities — data access, domain services, sequential pipelines, multi-step workflows — each deserving a separate layer. Infrastructure expanded beyond foundation utilities to include decorator-wrapped external clients and repository abstractions. Interface contracts needed a dedicated location to prevent circular dependencies between layers.</p>
<p>The evolution produced a nine-layer model that preserves Kennedy&rsquo;s core insight — dependency flows downward through physical boundaries — while providing finer granularity for complex systems.</p>
<h2 id="the-layer-model">
  The Layer Model
  <a class="heading-link" href="#the-layer-model">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The ideal structure separates concerns into nine layers, each with a single responsibility and clear dependency constraints. Lower layers provide primitives and infrastructure. Middle layers implement domain logic. Upper layers coordinate and expose functionality. Dependency arrows point strictly downward.</p>
<div class="mermaid">
graph TD
    E[Entrypoints<br/>DI wiring, bootstrap] --> C[Controllers<br/>HTTP/gRPC handlers]
    C --> W[Workflows<br/>Multi-step orchestration]
    W --> P[Pipelines<br/>Sequential transformations]
    W --> S[Services<br/>Business logic]
    S --> R[Repositories<br/>Data access]
    S --> CL[Clients<br/>External systems]
    R --> F[Foundation<br/>Logger, metrics, cache]
    CL --> F
    R --> I[Core Interfaces<br/>Contracts]
    S --> I
    P --> I
    W --> I
    C --> I
    CL --> I
    F --> I

    style E fill:#e1f5ff
    style C fill:#fff3cd
    style W fill:#fff3cd
    style P fill:#d4edda
    style S fill:#d4edda
    style R fill:#d1ecf1
    style CL fill:#d1ecf1
    style F fill:#f8d7da
    style I fill:#e2e3e5

</div>

<h3 id="core-interfaces--contracts-before-implementations">
  Core Interfaces — Contracts Before Implementations
  <a class="heading-link" href="#core-interfaces--contracts-before-implementations">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Interface definitions live in a dedicated package, isolated from all implementations. Every layer imports from <code>core/interfaces/</code>, but <code>core/interfaces/</code> imports from nothing. This breaks circular dependencies and establishes a single source of truth for contracts.</p>
<p>In Go, this means a <code>pkg/go/core/interfaces/</code> directory containing only interface definitions and their documentation:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// pkg/go/core/interfaces/repository.go</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">interfaces</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Repository provides generic CRUD operations for any entity type T,</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// filtered by parameters P, and identified by ID.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Repository</span>[<span style="color:#a6e22e">T</span> <span style="color:#66d9ef">any</span>, <span style="color:#a6e22e">P</span> <span style="color:#66d9ef">any</span>, <span style="color:#a6e22e">ID</span> <span style="color:#a6e22e">comparable</span>] <span style="color:#66d9ef">interface</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Create</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">entity</span> <span style="color:#a6e22e">T</span>) (<span style="color:#a6e22e">T</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">GetByID</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">id</span> <span style="color:#a6e22e">ID</span>) (<span style="color:#a6e22e">T</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">GetAll</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">params</span> <span style="color:#a6e22e">P</span>) ([]<span style="color:#a6e22e">T</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Update</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">id</span> <span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">entity</span> <span style="color:#a6e22e">T</span>) (<span style="color:#a6e22e">T</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">DeleteByID</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">id</span> <span style="color:#a6e22e">ID</span>) <span style="color:#66d9ef">error</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Count</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">params</span> <span style="color:#a6e22e">P</span>) (<span style="color:#66d9ef">int64</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>In Python, the equivalent is an <code>src/interfaces/</code> package with abstract base classes:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># src/interfaces/repository.py</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> abc <span style="color:#f92672">import</span> ABC, abstractmethod
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> typing <span style="color:#f92672">import</span> Generic, TypeVar
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>T <span style="color:#f92672">=</span> TypeVar(<span style="color:#e6db74">&#39;T&#39;</span>)  <span style="color:#75715e"># Entity type</span>
</span></span><span style="display:flex;"><span>P <span style="color:#f92672">=</span> TypeVar(<span style="color:#e6db74">&#39;P&#39;</span>)  <span style="color:#75715e"># Query parameters</span>
</span></span><span style="display:flex;"><span>ID <span style="color:#f92672">=</span> TypeVar(<span style="color:#e6db74">&#39;ID&#39;</span>)  <span style="color:#75715e"># Identifier type</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Repository</span>(ABC, Generic[T, P, ID]):
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;Generic repository interface for CRUD operations.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@abstractmethod</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">create</span>(self, entity: T, <span style="color:#f92672">*</span>, ctx: Ctx <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>) <span style="color:#f92672">-&gt;</span> T:
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;&#34;&#34;Create a new entity.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@abstractmethod</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_by_id</span>(self, entity_id: ID, <span style="color:#f92672">*</span>, ctx: Ctx <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>) <span style="color:#f92672">-&gt;</span> T <span style="color:#f92672">|</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;&#34;&#34;Retrieve an entity by its unique identifier.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@abstractmethod</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_all</span>(self, params: P, <span style="color:#f92672">*</span>, ctx: Ctx <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>) <span style="color:#f92672">-&gt;</span> list[T]:
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;&#34;&#34;Retrieve all entities matching the given parameters.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">...</span>
</span></span></code></pre></div><p>The interface layer defines contracts for repositories, services, pipelines, workflows, controllers, clients, loggers, metrics emitters, cache adapters, and all other abstractions. Implementations live elsewhere. This separation enables <strong>testability through mocking</strong> — a service test can inject a fake repository that implements the <code>Repository[T, P, ID]</code> interface without requiring a real database — and <strong>pluggability</strong> — swapping PostgreSQL for MongoDB requires only a different repository implementation, with zero changes to service logic.</p>
<h3 id="foundation--cross-cutting-infrastructure">
  Foundation — Cross-Cutting Infrastructure
  <a class="heading-link" href="#foundation--cross-cutting-infrastructure">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>The foundation layer provides utilities and adapters for logging, metrics collection, distributed tracing, caching, retry logic, circuit breaking, rate limiting, timeout enforcement, and other infrastructure concerns. Each component is independently versioned and publishable, depends only on core interfaces, and has zero dependencies on domain logic.</p>
<p>A logger in the foundation layer implements the <code>Logger</code> interface from core:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// pkg/go/foundation/logger/logger.go</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">logger</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;github.com/org/mothership/pkg/go/core/interfaces&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Config</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Kind</span>        <span style="color:#a6e22e">Kind</span>   <span style="color:#75715e">// stdlib, zap, zerolog</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Level</span>       <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">JSON</span>        <span style="color:#66d9ef">bool</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Development</span> <span style="color:#66d9ef">bool</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">kind</span> <span style="color:#a6e22e">Kind</span>, <span style="color:#a6e22e">opts</span> <span style="color:#f92672">...</span><span style="color:#a6e22e">Option</span>) <span style="color:#a6e22e">interfaces</span>.<span style="color:#a6e22e">Logger</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">cfg</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">DefaultConfig</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">opt</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">opts</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">opt</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">cfg</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">newFromConfig</span>(<span style="color:#a6e22e">cfg</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The foundation logger has three implementations: <code>stdlib</code> (Go&rsquo;s <code>log/slog</code>), <code>zap</code> (Uber&rsquo;s structured logger), and <code>zerolog</code> (zero-allocation logger). Each implementation satisfies the <code>interfaces.Logger</code> contract. Application code depends on the interface, not the implementation, so switching from <code>zap</code> to <code>zerolog</code> requires changing only the DI wiring, not every log call site.</p>
<p>Foundation components are <strong>independently versioned Go modules</strong>. Each has its own <code>go.mod</code>, <code>VERSION</code> file, and <code>CHANGELOG.md</code>. The logger is publishable as <code>github.com/org/mothership/foundation/logger@v1.2.0</code>, the metrics emitter as <code>github.com/org/mothership/foundation/metrics@v1.1.0</code>. Teams can upgrade the logger without touching the circuit breaker, and external projects can depend on individual foundation modules without pulling the entire monorepo.</p>
<h3 id="clients--external-system-integration">
  Clients — External System Integration
  <a class="heading-link" href="#clients--external-system-integration">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>The client layer wraps all interactions with external systems — databases, message brokers, object storage, third-party APIs. Clients implement interfaces from the core layer and are wrapped with decorator stacks for observability and resilience.</p>
<p>A MongoDB client in Python demonstrates the pattern:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># src/clients/domain/mongodb/client.py</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> motor.motor_asyncio <span style="color:#f92672">import</span> AsyncIOMotorClient
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> src.interfaces.client <span style="color:#f92672">import</span> DatabaseClient
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MongoDBClient</span>(DatabaseClient):
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;Async MongoDB client implementation.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">__init__</span>(self, uri: str, database: str) <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_client <span style="color:#f92672">=</span> AsyncIOMotorClient(uri)
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_db <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>_client[database]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">connect</span>(self) <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">await</span> self<span style="color:#f92672">.</span>_client<span style="color:#f92672">.</span>admin<span style="color:#f92672">.</span>command(<span style="color:#e6db74">&#39;ping&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">disconnect</span>(self) <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_client<span style="color:#f92672">.</span>close()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">collection</span>(self, name: str):
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> self<span style="color:#f92672">.</span>_db[name]
</span></span></code></pre></div><p>The raw client provides minimal functionality. Production use requires retry logic for transient failures, circuit breaking to prevent cascading failures, structured logging for observability, and metrics for monitoring. Decorators compose these concerns:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># src/clients/sdk/decorators.py</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> src.clients.sdk.base <span style="color:#f92672">import</span> ClientDecorator
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RetryDecorator</span>(ClientDecorator[T]):
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;Wraps a client with retry logic.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">__init__</span>(self, inner: T, retrier: Retrier) <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        super()<span style="color:#f92672">.</span><span style="color:#a6e22e">__init__</span>(inner)
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_retrier <span style="color:#f92672">=</span> retrier
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">execute</span>(self, operation, <span style="color:#f92672">*</span>args, <span style="color:#f92672">**</span>kwargs):
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">await</span> self<span style="color:#f92672">.</span>_retrier<span style="color:#f92672">.</span>execute(
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">lambda</span>: getattr(self<span style="color:#f92672">.</span>_inner, operation)(<span style="color:#f92672">*</span>args, <span style="color:#f92672">**</span>kwargs)
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CircuitBreakerDecorator</span>(ClientDecorator[T]):
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;Wraps a client with circuit breaking.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">__init__</span>(self, inner: T, circuit_breaker: CircuitBreaker) <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        super()<span style="color:#f92672">.</span><span style="color:#a6e22e">__init__</span>(inner)
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_cb <span style="color:#f92672">=</span> circuit_breaker
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">execute</span>(self, operation, <span style="color:#f92672">*</span>args, <span style="color:#f92672">**</span>kwargs):
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">await</span> self<span style="color:#f92672">.</span>_cb<span style="color:#f92672">.</span>call(
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">lambda</span>: getattr(self<span style="color:#f92672">.</span>_inner, operation)(<span style="color:#f92672">*</span>args, <span style="color:#f92672">**</span>kwargs)
</span></span><span style="display:flex;"><span>        )
</span></span></code></pre></div><p>A factory function composes the stack:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">create_mongo_client</span>(settings: Settings) <span style="color:#f92672">-&gt;</span> DatabaseClient:
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;Factory that wraps raw client with resilience and observability.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    raw <span style="color:#f92672">=</span> MongoDBClient(settings<span style="color:#f92672">.</span>mongo_uri, settings<span style="color:#f92672">.</span>database)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Inner: circuit breaker → retry</span>
</span></span><span style="display:flex;"><span>    with_retry <span style="color:#f92672">=</span> RetryDecorator(raw, create_retrier())
</span></span><span style="display:flex;"><span>    with_cb <span style="color:#f92672">=</span> CircuitBreakerDecorator(with_retry, create_circuit_breaker())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Outer: logging → metrics</span>
</span></span><span style="display:flex;"><span>    with_logging <span style="color:#f92672">=</span> LoggingDecorator(with_cb, logger)
</span></span><span style="display:flex;"><span>    with_metrics <span style="color:#f92672">=</span> MetricsDecorator(with_logging, metrics)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> with_metrics
</span></span></code></pre></div><p>The resulting client satisfies the <code>DatabaseClient</code> interface while transparently adding retry, circuit breaking, logging, and metrics. Service code calls <code>client.collection('users').find({})</code> without awareness of the decorator stack. For the full decorator pattern details, see my earlier post on <a href="/posts/decorator-pattern-observability-resilience/" target="_blank">the decorator pattern for observability and resilience</a>.</p>
<h3 id="repositories--data-access-abstraction">
  Repositories — Data Access Abstraction
  <a class="heading-link" href="#repositories--data-access-abstraction">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Repositories provide a domain-oriented interface to persistent storage. A repository accepts domain entities (not database-specific persistence models), translates them to the appropriate storage format, and abstracts query construction.</p>
<p>Go repositories are generic over entity type, query parameters, and identifier:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// pkg/go/platform/repository/repository.go</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">repository</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">BaseRepository</span>[<span style="color:#a6e22e">T</span> <span style="color:#66d9ef">any</span>, <span style="color:#a6e22e">P</span> <span style="color:#66d9ef">any</span>, <span style="color:#a6e22e">ID</span> <span style="color:#a6e22e">comparable</span>] <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">tableName</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">db</span>        <span style="color:#a6e22e">interfaces</span>.<span style="color:#a6e22e">DatabaseClient</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">BaseRepository</span>[<span style="color:#a6e22e">T</span>, <span style="color:#a6e22e">P</span>, <span style="color:#a6e22e">ID</span>]) <span style="color:#a6e22e">GetByID</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">id</span> <span style="color:#a6e22e">ID</span>,
</span></span><span style="display:flex;"><span>) (<span style="color:#a6e22e">T</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">zero</span> <span style="color:#a6e22e">T</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">query</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;SELECT * FROM %s WHERE id = $1&#34;</span>, <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">tableName</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Execute query, scan into entity, return</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Concrete repositories embed <code>BaseRepository</code> and add domain-specific queries:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// apps/go/server/internal/repositories/user_repository.go</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">repositories</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">UserRepository</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">*</span><span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">BaseRepository</span>[<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">UserParams</span>, <span style="color:#a6e22e">uuid</span>.<span style="color:#a6e22e">UUID</span>]
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewUserRepository</span>(<span style="color:#a6e22e">db</span> <span style="color:#a6e22e">interfaces</span>.<span style="color:#a6e22e">DatabaseClient</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">UserRepository</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">UserRepository</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">BaseRepository</span>: <span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">NewBase</span>[<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">UserParams</span>, <span style="color:#a6e22e">uuid</span>.<span style="color:#a6e22e">UUID</span>](
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;users&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">db</span>,
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">UserRepository</span>) <span style="color:#a6e22e">GetByEmail</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">email</span> <span style="color:#66d9ef">string</span>,
</span></span><span style="display:flex;"><span>) (<span style="color:#a6e22e">User</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Custom query beyond base CRUD</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Python repositories follow the same pattern with ABCs:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># src/repository/mongo/transcript.py</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> src.interfaces.repository <span style="color:#f92672">import</span> Repository
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> src.repository.mongo.models <span style="color:#f92672">import</span> TranscriptMongoModel
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TranscriptRepository</span>(Repository[Transcript, TranscriptQueryParams, str]):
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;MongoDB repository for transcripts.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">__init__</span>(self, client: DatabaseClient) <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_collection <span style="color:#f92672">=</span> client<span style="color:#f92672">.</span>collection(<span style="color:#e6db74">&#39;transcripts&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_by_id</span>(
</span></span><span style="display:flex;"><span>        self,
</span></span><span style="display:flex;"><span>        entity_id: str,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">*</span>,
</span></span><span style="display:flex;"><span>        ctx: Ctx <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
</span></span><span style="display:flex;"><span>    ) <span style="color:#f92672">-&gt;</span> Transcript <span style="color:#f92672">|</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        doc <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> self<span style="color:#f92672">.</span>_collection<span style="color:#f92672">.</span>find_one({<span style="color:#e6db74">&#39;_id&#39;</span>: entity_id})
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> doc:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">None</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> self<span style="color:#f92672">.</span>_translator<span style="color:#f92672">.</span>to_domain(doc)
</span></span></code></pre></div><p>Repositories depend on clients (the layer below) and implement the <code>Repository[T, P, ID]</code> interface from core. They are <strong>database-agnostic from the service layer&rsquo;s perspective</strong> — swapping MongoDB for PostgreSQL requires only injecting a different repository implementation at the composition root, with zero service code changes.</p>
<h3 id="services--business-logic">
  Services — Business Logic
  <a class="heading-link" href="#services--business-logic">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Services implement domain logic and business rules. They are stateless, depend on repositories and clients through interfaces, and expose methods that operate on domain entities.</p>
<p>A service in Go:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// pkg/go/services/corpus/service.go</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">corpus</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Service</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">transcriptRepo</span> <span style="color:#a6e22e">interfaces</span>.<span style="color:#a6e22e">Repository</span>[<span style="color:#a6e22e">Transcript</span>, <span style="color:#a6e22e">TranscriptParams</span>, <span style="color:#66d9ef">string</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">speakerRepo</span>    <span style="color:#a6e22e">interfaces</span>.<span style="color:#a6e22e">Repository</span>[<span style="color:#a6e22e">Speaker</span>, <span style="color:#a6e22e">SpeakerParams</span>, <span style="color:#66d9ef">string</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">logger</span>         <span style="color:#a6e22e">interfaces</span>.<span style="color:#a6e22e">Logger</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">s</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Service</span>) <span style="color:#a6e22e">GetTranscriptWithSpeakers</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">transcriptID</span> <span style="color:#66d9ef">string</span>,
</span></span><span style="display:flex;"><span>) (<span style="color:#a6e22e">TranscriptWithSpeakers</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Business logic: fetch transcript, fetch related speakers, assemble</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">transcript</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">transcriptRepo</span>.<span style="color:#a6e22e">GetByID</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">transcriptID</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">TranscriptWithSpeakers</span>{}, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;fetch transcript: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">speakerIDs</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">transcript</span>.<span style="color:#a6e22e">SpeakerIDs</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">speakers</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">speakerRepo</span>.<span style="color:#a6e22e">GetAll</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">SpeakerParams</span>{<span style="color:#a6e22e">IDs</span>: <span style="color:#a6e22e">speakerIDs</span>})
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">TranscriptWithSpeakers</span>{}, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;fetch speakers: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">assembleTranscriptWithSpeakers</span>(<span style="color:#a6e22e">transcript</span>, <span style="color:#a6e22e">speakers</span>), <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Services are wrapped with the same decorator stack as clients — logging, metrics, retry, circuit breaking. A <code>ServiceBuilder</code> composes decorators:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># src/service/sdk/builder.py</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> src.interfaces.builder <span style="color:#f92672">import</span> Builder
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ServiceBuilder</span>(Builder[T, C], Generic[T, C]):
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;Builds services with decorator composition.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">build</span>(self) <span style="color:#f92672">-&gt;</span> T:
</span></span><span style="display:flex;"><span>        instance <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>_create_instance()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Apply fault tolerance decorators (innermost)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> self<span style="color:#f92672">.</span>_fault_tolerance:
</span></span><span style="display:flex;"><span>            instance <span style="color:#f92672">=</span> RetryDecorator(instance, self<span style="color:#f92672">.</span>_retrier)
</span></span><span style="display:flex;"><span>            instance <span style="color:#f92672">=</span> CircuitBreakerDecorator(instance, self<span style="color:#f92672">.</span>_cb)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Apply observability decorators (outermost)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> self<span style="color:#f92672">.</span>_observability:
</span></span><span style="display:flex;"><span>            instance <span style="color:#f92672">=</span> LoggingDecorator(instance, self<span style="color:#f92672">.</span>_logger)
</span></span><span style="display:flex;"><span>            instance <span style="color:#f92672">=</span> MetricsDecorator(instance, self<span style="color:#f92672">.</span>_metrics)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> instance
</span></span></code></pre></div><p>Services depend on repositories and clients (layers below) and implement service interfaces from core. They do not depend on workflows, controllers, or entrypoints (layers above).</p>
<h3 id="pipelines--sequential-transformations">
  Pipelines — Sequential Transformations
  <a class="heading-link" href="#pipelines--sequential-transformations">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Pipelines model sequential data transformations where each stage consumes the output of the prior stage. A pipeline implements <code>Pipeline[In, Out]</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// pkg/go/core/interfaces/pipeline.go</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">interfaces</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Pipeline</span>[<span style="color:#a6e22e">In</span> <span style="color:#66d9ef">any</span>, <span style="color:#a6e22e">Out</span> <span style="color:#66d9ef">any</span>] <span style="color:#66d9ef">interface</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">input</span> <span style="color:#a6e22e">In</span>) (<span style="color:#a6e22e">Out</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>An NLP pipeline in Python demonstrates composition:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># src/pipeline/domain/nlp/spacy_pipeline.py</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> src.interfaces.pipeline <span style="color:#f92672">import</span> Pipeline
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SpacyPipeline</span>(Pipeline[str, ProcessedDocument]):
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;NLP processing pipeline using spaCy.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">__init__</span>(self, model: str <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;en_core_web_sm&#34;</span>) <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_nlp <span style="color:#f92672">=</span> spacy<span style="color:#f92672">.</span>load(model)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">execute</span>(self, input: str, <span style="color:#f92672">*</span>, ctx: Ctx <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>) <span style="color:#f92672">-&gt;</span> ProcessedDocument:
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;&#34;&#34;Process text through: tokenization → POS → NER → dep parse.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        doc <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>_nlp(input)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> ProcessedDocument(
</span></span><span style="display:flex;"><span>            text<span style="color:#f92672">=</span>input,
</span></span><span style="display:flex;"><span>            tokens<span style="color:#f92672">=</span>[token<span style="color:#f92672">.</span>text <span style="color:#66d9ef">for</span> token <span style="color:#f92672">in</span> doc],
</span></span><span style="display:flex;"><span>            pos_tags<span style="color:#f92672">=</span>[token<span style="color:#f92672">.</span>pos_ <span style="color:#66d9ef">for</span> token <span style="color:#f92672">in</span> doc],
</span></span><span style="display:flex;"><span>            entities<span style="color:#f92672">=</span>[(ent<span style="color:#f92672">.</span>text, ent<span style="color:#f92672">.</span>label_) <span style="color:#66d9ef">for</span> ent <span style="color:#f92672">in</span> doc<span style="color:#f92672">.</span>ents],
</span></span><span style="display:flex;"><span>            dependencies<span style="color:#f92672">=</span>[(token<span style="color:#f92672">.</span>text, token<span style="color:#f92672">.</span>dep_, token<span style="color:#f92672">.</span>head<span style="color:#f92672">.</span>text) <span style="color:#66d9ef">for</span> token <span style="color:#f92672">in</span> doc],
</span></span><span style="display:flex;"><span>        )
</span></span></code></pre></div><p>Pipelines are stateless transformations. They depend on services or clients for data fetching but do not coordinate multi-step workflows. That responsibility belongs to the next layer.</p>
<h3 id="workflows--multi-step-orchestration">
  Workflows — Multi-Step Orchestration
  <a class="heading-link" href="#workflows--multi-step-orchestration">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Workflows coordinate multiple operations — fetch data, validate, transform, publish events, handle errors. A workflow implements <code>Workflow[In, Out]</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// pkg/go/core/interfaces/workflow.go</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">interfaces</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Workflow</span>[<span style="color:#a6e22e">In</span> <span style="color:#66d9ef">any</span>, <span style="color:#a6e22e">Out</span> <span style="color:#66d9ef">any</span>] <span style="color:#66d9ef">interface</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">input</span> <span style="color:#a6e22e">In</span>) (<span style="color:#a6e22e">Out</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>A search workflow demonstrates cache-aside pattern:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># src/workflow/domain/search/workflow.py</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> src.interfaces.workflow <span style="color:#f92672">import</span> AsyncWorkflow
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SearchWorkflow</span>(AsyncWorkflow[SearchRequest, SearchResponse]):
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;Implements cache-aside search: check cache, else pipeline.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">__init__</span>(
</span></span><span style="display:flex;"><span>        self,
</span></span><span style="display:flex;"><span>        cache: Cache,
</span></span><span style="display:flex;"><span>        pipeline: AsyncPipeline[SearchRequest, SearchResponse],
</span></span><span style="display:flex;"><span>    ) <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_cache <span style="color:#f92672">=</span> cache
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_pipeline <span style="color:#f92672">=</span> pipeline
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">execute</span>(
</span></span><span style="display:flex;"><span>        self,
</span></span><span style="display:flex;"><span>        input: SearchRequest,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">*</span>,
</span></span><span style="display:flex;"><span>        ctx: Ctx <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
</span></span><span style="display:flex;"><span>    ) <span style="color:#f92672">-&gt;</span> SearchResponse:
</span></span><span style="display:flex;"><span>        cache_key <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>_build_cache_key(input)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Cache hit?</span>
</span></span><span style="display:flex;"><span>        cached <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> self<span style="color:#f92672">.</span>_cache<span style="color:#f92672">.</span>get(cache_key)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> cached:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> SearchResponse<span style="color:#f92672">.</span>from_json(cached)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Cache miss — execute pipeline</span>
</span></span><span style="display:flex;"><span>        result <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> self<span style="color:#f92672">.</span>_pipeline<span style="color:#f92672">.</span>execute(input, ctx<span style="color:#f92672">=</span>ctx)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Cache result</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">await</span> self<span style="color:#f92672">.</span>_cache<span style="color:#f92672">.</span>set(cache_key, result<span style="color:#f92672">.</span>to_json(), ttl<span style="color:#f92672">=</span><span style="color:#ae81ff">300</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> result
</span></span></code></pre></div><p>Workflows depend on services, pipelines, repositories, and clients. They do not depend on controllers or entrypoints.</p>
<h3 id="controllers--httpgrpc-handlers">
  Controllers — HTTP/gRPC Handlers
  <a class="heading-link" href="#controllers--httpgrpc-handlers">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Controllers are thin adapters between transport protocols (HTTP, gRPC, GraphQL) and workflows. They deserialize requests into DTOs, invoke workflows, and serialize responses.</p>
<p>A FastAPI controller in Python:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># src/controller/domain/search/controller.py</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> fastapi <span style="color:#f92672">import</span> APIRouter
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> src.interfaces.workflow <span style="color:#f92672">import</span> AsyncWorkflow
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SearchController</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;HTTP controller for search endpoints.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">__init__</span>(
</span></span><span style="display:flex;"><span>        self,
</span></span><span style="display:flex;"><span>        workflow: AsyncWorkflow[SearchRequest, SearchResponse],
</span></span><span style="display:flex;"><span>    ) <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_workflow <span style="color:#f92672">=</span> workflow
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>router <span style="color:#f92672">=</span> APIRouter()
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>router<span style="color:#f92672">.</span>add_api_route(<span style="color:#e6db74">&#34;/search&#34;</span>, self<span style="color:#f92672">.</span>search, methods<span style="color:#f92672">=</span>[<span style="color:#e6db74">&#34;GET&#34;</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">search</span>(
</span></span><span style="display:flex;"><span>        self,
</span></span><span style="display:flex;"><span>        query: str,
</span></span><span style="display:flex;"><span>        filters: str <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>,
</span></span><span style="display:flex;"><span>        limit: int <span style="color:#f92672">=</span> <span style="color:#ae81ff">10</span>,
</span></span><span style="display:flex;"><span>    ) <span style="color:#f92672">-&gt;</span> SearchResponse:
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;&#34;&#34;GET /search endpoint.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        request <span style="color:#f92672">=</span> SearchRequest(query<span style="color:#f92672">=</span>query, filters<span style="color:#f92672">=</span>filters, limit<span style="color:#f92672">=</span>limit)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">await</span> self<span style="color:#f92672">.</span>_workflow<span style="color:#f92672">.</span>execute(request)
</span></span></code></pre></div><p>Controllers depend on workflows (the layer below). They do not contain business logic. Validation, transformation, and orchestration belong in lower layers.</p>
<h3 id="entrypoints--di-wiring-and-bootstrap">
  Entrypoints — DI Wiring and Bootstrap
  <a class="heading-link" href="#entrypoints--di-wiring-and-bootstrap">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Entrypoints are composition roots. They load configuration, instantiate dependencies, wire the DI container, and start the server. Each microservice has an entrypoint:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># entrypoints/server/search/container.py</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> dependency_injector <span style="color:#f92672">import</span> containers, providers
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> src.foundation.config <span style="color:#f92672">import</span> CoreContainer
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SearchContainer</span>(containers<span style="color:#f92672">.</span>DeclarativeContainer):
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;DI container for search server.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Inherit core infrastructure</span>
</span></span><span style="display:flex;"><span>    core <span style="color:#f92672">=</span> providers<span style="color:#f92672">.</span>DependenciesContainer()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Repository</span>
</span></span><span style="display:flex;"><span>    es_repo <span style="color:#f92672">=</span> providers<span style="color:#f92672">.</span>Singleton(
</span></span><span style="display:flex;"><span>        AsyncElasticTranscriptRepository,
</span></span><span style="display:flex;"><span>        client<span style="color:#f92672">=</span>core<span style="color:#f92672">.</span>es_client,
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Pipeline</span>
</span></span><span style="display:flex;"><span>    search_pipeline <span style="color:#f92672">=</span> providers<span style="color:#f92672">.</span>Singleton(
</span></span><span style="display:flex;"><span>        SearchPipeline,
</span></span><span style="display:flex;"><span>        repository<span style="color:#f92672">=</span>es_repo,
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Workflow</span>
</span></span><span style="display:flex;"><span>    search_workflow <span style="color:#f92672">=</span> providers<span style="color:#f92672">.</span>Singleton(
</span></span><span style="display:flex;"><span>        SearchWorkflow,
</span></span><span style="display:flex;"><span>        cache<span style="color:#f92672">=</span>core<span style="color:#f92672">.</span>redis_client,
</span></span><span style="display:flex;"><span>        pipeline<span style="color:#f92672">=</span>search_pipeline,
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Controller</span>
</span></span><span style="display:flex;"><span>    search_controller <span style="color:#f92672">=</span> providers<span style="color:#f92672">.</span>Singleton(
</span></span><span style="display:flex;"><span>        SearchController,
</span></span><span style="display:flex;"><span>        workflow<span style="color:#f92672">=</span>search_workflow,
</span></span><span style="display:flex;"><span>    )
</span></span></code></pre></div><p>Entrypoints depend on every other layer. No other layer depends on entrypoints. This is the <strong>dependency inversion principle</strong> in physical form — high-level policy (entrypoints) depends on low-level details (services, repositories), but low-level details do not depend back.</p>
<h2 id="the-dependency-rule">
  The Dependency Rule
  <a class="heading-link" href="#the-dependency-rule">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Dependency direction is the single most important structural constraint. Code in layer N can depend on layers N-1, N-2, &hellip; all the way down to core interfaces. Code in layer N <strong>cannot</strong> depend on layers N+1, N+2, or any layer above.</p>
<div class="mermaid">
graph TD
    subgraph "Allowed Dependencies (Downward)"
        E1[Entrypoint] -->|✓| C1[Controller]
        C1 -->|✓| W1[Workflow]
        W1 -->|✓| S1[Service]
        S1 -->|✓| R1[Repository]
        R1 -->|✓| F1[Foundation]
    end

    subgraph "Forbidden Dependencies (Upward/Sideways)"
        S2[Service] -.->|✗| W2[Workflow]
        R2[Repository] -.->|✗| S2[Service]
        F2[Foundation] -.->|✗| R2[Repository]
        W3[Workflow] -.->|✗| C2[Controller]
    end

    style E1 fill:#e1f5ff
    style C1 fill:#fff3cd
    style C2 fill:#fff3cd
    style W1 fill:#d4edda
    style W2 fill:#d4edda
    style W3 fill:#d4edda
    style S1 fill:#d1ecf1
    style S2 fill:#d1ecf1
    style R1 fill:#f8d7da
    style R2 fill:#f8d7da
    style F1 fill:#e2e3e5
    style F2 fill:#e2e3e5

</div>

<h3 id="enforcement-in-go">
  Enforcement in Go
  <a class="heading-link" href="#enforcement-in-go">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Go enforces the dependency rule through import paths. A service in <code>pkg/go/services/corpus/</code> can import from <code>pkg/go/repos/</code> and <code>pkg/go/foundation/</code>, but attempting to import <code>pkg/go/workflow/</code> or <code>pkg/go/controllers/</code> produces a compilation error if those packages do not exist at lower layers.</p>
<p>Compile-time checks make violations impossible. A repository that tries to emit domain events by importing the event bus triggers:</p>
<pre tabindex="0"><code>pkg/go/repos/postgres/user_repository.go:5:2:
    cannot import &#34;github.com/org/mothership/pkg/go/workflow/events&#34;
    (import cycle or dependency rule violation)
</code></pre><p>Static analysis tools like <code>go mod graph | grep</code> can detect cross-layer violations:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>go mod graph | grep <span style="color:#e6db74">&#34;repos.*-&gt;.*services&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Output: (empty if no violations)</span>
</span></span></code></pre></div><h3 id="enforcement-in-python">
  Enforcement in Python
  <a class="heading-link" href="#enforcement-in-python">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Python does not prevent circular imports at compile time. Import linting tools like <code>import-linter</code> enforce the dependency rule:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#75715e"># pyproject.toml</span>
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">tool</span>.<span style="color:#a6e22e">importlinter</span>]
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">root_package</span> = <span style="color:#e6db74">&#34;src&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[[<span style="color:#a6e22e">tool</span>.<span style="color:#a6e22e">importlinter</span>.<span style="color:#a6e22e">contracts</span>]]
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">name</span> = <span style="color:#e6db74">&#34;Layered architecture&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">type</span> = <span style="color:#e6db74">&#34;layers&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">layers</span> = [
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;entrypoints&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;controller&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;workflow&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;pipeline&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;service&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;repository | clients&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;foundation&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;interfaces&#34;</span>,
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>Running <code>lint-imports</code> fails the build if a service imports from workflow:</p>
<pre tabindex="0"><code>Layer violation: src.service.corpus imported src.workflow.search
  Layers: service -&gt; workflow (forbidden upward dependency)
</code></pre><p>Directory structure reinforces the rule. A developer attempting to import <code>from workflow.search import SearchWorkflow</code> into a service file sees the import path and realizes the violation before running linters.</p>
<h2 id="interface-first-design">
  Interface-First Design
  <a class="heading-link" href="#interface-first-design">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Defining all interfaces in a dedicated package prevents circular dependencies and establishes a contract-first development model. Implementation layers import interface definitions, but the interface package imports nothing.</p>
<h3 id="go-compile-time-verification">
  Go: Compile-Time Verification
  <a class="heading-link" href="#go-compile-time-verification">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Go repositories provide interface implementations:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// pkg/go/repos/postgres/user_repository.go</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">postgres</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;github.com/org/mothership/pkg/go/core/interfaces&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">UserRepository</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">db</span> <span style="color:#a6e22e">interfaces</span>.<span style="color:#a6e22e">DatabaseClient</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Compile-time assertion: UserRepository implements Repository[User, UserParams, uuid.UUID]</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">_</span> <span style="color:#a6e22e">interfaces</span>.<span style="color:#a6e22e">Repository</span>[<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">UserParams</span>, <span style="color:#a6e22e">uuid</span>.<span style="color:#a6e22e">UUID</span>] = (<span style="color:#f92672">*</span><span style="color:#a6e22e">UserRepository</span>)(<span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">UserRepository</span>) <span style="color:#a6e22e">GetByID</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">id</span> <span style="color:#a6e22e">uuid</span>.<span style="color:#a6e22e">UUID</span>,
</span></span><span style="display:flex;"><span>) (<span style="color:#a6e22e">User</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Implementation</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The assertion <code>var _ interfaces.Repository[...] = (*UserRepository)(nil)</code> is checked at compile time. If <code>UserRepository</code> fails to implement any method from <code>Repository[T, P, ID]</code>, compilation fails. Interfaces are self-documenting contracts with compiler enforcement.</p>
<h3 id="python-runtime-protocol-checks">
  Python: Runtime Protocol Checks
  <a class="heading-link" href="#python-runtime-protocol-checks">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Python repositories use ABCs for interface definitions:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># src/interfaces/repository.py</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> abc <span style="color:#f92672">import</span> ABC, abstractmethod
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> typing <span style="color:#f92672">import</span> Generic, TypeVar, runtime_checkable, Protocol
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>T <span style="color:#f92672">=</span> TypeVar(<span style="color:#e6db74">&#39;T&#39;</span>)
</span></span><span style="display:flex;"><span>P <span style="color:#f92672">=</span> TypeVar(<span style="color:#e6db74">&#39;P&#39;</span>)
</span></span><span style="display:flex;"><span>ID <span style="color:#f92672">=</span> TypeVar(<span style="color:#e6db74">&#39;ID&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@runtime_checkable</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Repository</span>(Protocol, Generic[T, P, ID]):
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;Repository protocol for CRUD operations.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_by_id</span>(self, entity_id: ID, <span style="color:#f92672">*</span>, ctx: Ctx <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>) <span style="color:#f92672">-&gt;</span> T <span style="color:#f92672">|</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">...</span>
</span></span></code></pre></div><p>Concrete implementations inherit and override:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># src/repository/mongo/transcript.py</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> src.interfaces.repository <span style="color:#f92672">import</span> Repository
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TranscriptRepository</span>(Repository[Transcript, TranscriptQueryParams, str]):
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;MongoDB transcript repository.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_by_id</span>(
</span></span><span style="display:flex;"><span>        self,
</span></span><span style="display:flex;"><span>        entity_id: str,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">*</span>,
</span></span><span style="display:flex;"><span>        ctx: Ctx <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
</span></span><span style="display:flex;"><span>    ) <span style="color:#f92672">-&gt;</span> Transcript <span style="color:#f92672">|</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Implementation</span>
</span></span></code></pre></div><p>Type checkers like Mypy verify protocol adherence at static analysis time. The <code>@runtime_checkable</code> decorator enables <code>isinstance()</code> checks:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>repo <span style="color:#f92672">=</span> TranscriptRepository(client)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">assert</span> isinstance(repo, Repository)  <span style="color:#75715e"># True</span>
</span></span></code></pre></div><h3 id="why-dedicated-interface-packages-matter">
  Why Dedicated Interface Packages Matter
  <a class="heading-link" href="#why-dedicated-interface-packages-matter">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Without a separate interface package, repositories and services create circular dependencies. A service needs <code>RepositoryInterface</code>, so it imports from the repository package. The repository needs <code>ServiceInterface</code> for callbacks, so it imports from the service package. The import cycle breaks the build.</p>
<p>A dedicated <code>core/interfaces/</code> package (Go) or <code>interfaces/</code> package (Python) solves this:</p>
<ul>
<li>Services import <code>interfaces.Repository</code></li>
<li>Repositories import <code>interfaces.Repository</code> and implement it</li>
<li>No service-to-repository or repository-to-service import required</li>
</ul>
<p>Interfaces become the shared contract. Changes to repository implementations do not require service code changes if the interface remains stable.</p>
<h2 id="the-monorepo-layout">
  The Monorepo Layout
  <a class="heading-link" href="#the-monorepo-layout">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The layer model maps to directory structure. Go and Python monorepos follow parallel organization with language-appropriate conventions.</p>
<h3 id="go-mothership">
  Go: mothership
  <a class="heading-link" href="#go-mothership">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<pre tabindex="0"><code>mothership/
├── pkg/
│   └── go/
│       ├── core/
│       │   ├── interfaces/      # 60+ interface definitions
│       │   ├── types/           # Option, Either, Result, Page, ID
│       │   └── errors/          # Core error types
│       │
│       ├── foundation/          # ~20 independent modules
│       │   ├── logger/          # stdlib, zap, zerolog
│       │   │   ├── go.mod       # Independent versioning
│       │   │   ├── VERSION
│       │   │   ├── base/
│       │   │   ├── noop/
│       │   │   ├── stdlib/
│       │   │   ├── zap/
│       │   │   └── zerolog/
│       │   ├── metrics/         # Prometheus, OTEL
│       │   ├── cache/           # Redis, Ristretto
│       │   ├── circuitbreaker/
│       │   ├── retrier/
│       │   └── ...
│       │
│       ├── repos/               # Data access implementations
│       │   ├── databases/
│       │   │   ├── relational/  # PostgreSQL, SQLite
│       │   │   ├── time-series/ # InfluxDB, TimescaleDB
│       │   │   └── document/    # MongoDB
│       │   └── caches/          # Redis, Ristretto
│       │
│       ├── services/            # Domain services
│       │   ├── git/
│       │   └── version/
│       │
│       ├── platform/            # Request processing layers
│       │   ├── pipeline/
│       │   ├── decorators/
│       │   └── workflow/
│       │
│       └── config/              # Config providers
│
└── apps/
    └── go/
        ├── server/              # Main API server
        │   ├── cmd/main.go      # Bootstrap
        │   ├── internal/
        │   │   ├── handlers/
        │   │   ├── services/
        │   │   ├── repositories/
        │   │   └── wire/        # Google Wire DI
        │   └── configs/
        ├── worker/              # Background worker
        └── job/                 # Batch job
</code></pre><h3 id="python-hpc">
  Python: hpc
  <a class="heading-link" href="#python-hpc">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<pre tabindex="0"><code>hpc/
├── src/
│   ├── interfaces/          # ABCs for all contracts
│   │   ├── repository.py
│   │   ├── service.py
│   │   ├── pipeline.py
│   │   ├── workflow.py
│   │   └── ...
│   │
│   ├── foundation/          # Infrastructure
│   │   ├── logger/
│   │   ├── metrics/
│   │   ├── cache/
│   │   ├── resilience/      # Retry, circuit breaker
│   │   └── ...
│   │
│   ├── clients/             # External systems
│   │   ├── sdk/             # Decorator framework
│   │   │   ├── base.py
│   │   │   ├── retry.py
│   │   │   ├── circuit_breaker.py
│   │   │   └── logging.py
│   │   └── domain/          # Concrete clients
│   │       ├── mongodb/
│   │       ├── redis/
│   │       ├── elasticsearch/
│   │       └── kafka/
│   │
│   ├── repository/          # Data access
│   │   ├── sdk/             # Base repository
│   │   └── mongo/           # MongoDB repositories
│   │
│   ├── service/             # Business logic
│   │   ├── sdk/
│   │   └── domain/
│   │
│   ├── pipeline/            # Sequential transformations
│   │   ├── sdk/
│   │   └── domain/
│   │
│   ├── workflow/            # Multi-step orchestration
│   │   ├── sdk/
│   │   └── domain/
│   │
│   └── controller/          # HTTP handlers
│       ├── sdk/
│       └── domain/
│
└── entrypoints/             # Service wiring
    ├── server/
    │   ├── search/          # Search API
    │   │   ├── container.py # DI wiring
    │   │   ├── app.py       # FastAPI factory
    │   │   └── routes/
    │   ├── admin/           # Admin API
    │   └── auth/            # Auth API
    ├── stream/              # Kafka consumers
    │   ├── processor/
    │   └── indexer/
    └── cli/                 # Typer CLI
</code></pre><h3 id="parallel-structure">
  Parallel Structure
  <a class="heading-link" href="#parallel-structure">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Both repositories follow the same conceptual layers with language-appropriate naming:</p>
<table>
  <thead>
      <tr>
          <th>Layer</th>
          <th>Go</th>
          <th>Python</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Interfaces</td>
          <td><code>pkg/go/core/interfaces/</code></td>
          <td><code>src/interfaces/</code></td>
      </tr>
      <tr>
          <td>Foundation</td>
          <td><code>pkg/go/foundation/</code></td>
          <td><code>src/foundation/</code></td>
      </tr>
      <tr>
          <td>Clients</td>
          <td><code>pkg/go/clients/</code></td>
          <td><code>src/clients/</code></td>
      </tr>
      <tr>
          <td>Repositories</td>
          <td><code>pkg/go/repos/</code></td>
          <td><code>src/repository/</code></td>
      </tr>
      <tr>
          <td>Services</td>
          <td><code>pkg/go/services/</code></td>
          <td><code>src/service/</code></td>
      </tr>
      <tr>
          <td>Pipelines</td>
          <td><code>pkg/go/platform/pipeline/</code></td>
          <td><code>src/pipeline/</code></td>
      </tr>
      <tr>
          <td>Workflows</td>
          <td><code>pkg/go/platform/workflow/</code></td>
          <td><code>src/workflow/</code></td>
      </tr>
      <tr>
          <td>Controllers</td>
          <td>(in app)</td>
          <td><code>src/controller/</code></td>
      </tr>
      <tr>
          <td>Entrypoints</td>
          <td><code>apps/go/</code></td>
          <td><code>entrypoints/</code></td>
      </tr>
  </tbody>
</table>
<p>The directory structure makes dependency flow visible. Reading from top to bottom shows increasing abstraction. Reading imports shows dependency direction.</p>
<h2 id="decorator-composition-across-layers">
  Decorator Composition Across Layers
  <a class="heading-link" href="#decorator-composition-across-layers">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Each layer boundary is wrapped with decorators for logging, metrics, tracing, retry, and circuit breaking. A service calling a repository triggers the decorator stack at both boundaries:</p>
<pre tabindex="0"><code>Controller → [Logging → Metrics] → Workflow
                 ↓
Workflow → [Logging → Metrics] → Service
                 ↓
Service → [Logging → Metrics → Retry → CB] → Repository
                 ↓
Repository → [Logging → Metrics → Retry → CB] → Client
</code></pre><p>The decorator pattern separates cross-cutting concerns from business logic. For a detailed explanation of how decorators stack, see <a href="/posts/decorator-pattern-observability-resilience/" target="_blank">the decorator pattern for observability and resilience</a>.</p>
<p>Builders compose the decorator stack at each layer:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>service <span style="color:#f92672">=</span> (
</span></span><span style="display:flex;"><span>    CorpusServiceBuilder(config)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">.</span>with_repository(repo)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">.</span>with_observability(enabled<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">.</span>with_fault_tolerance(enabled<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">.</span>build()
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Returns: LoggingDecorator(MetricsDecorator(RetryDecorator(CircuitBreakerDecorator(raw_service))))</span>
</span></span></code></pre></div><p>Decorators apply uniformly across all services, repositories, and clients. Operational changes — adjusting retry backoff, switching metric backends, changing log formats — require modifications to decorator implementations, not to every service call site.</p>
<h2 id="what-the-structure-prevents">
  What the Structure Prevents
  <a class="heading-link" href="#what-the-structure-prevents">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Layers eliminate pathologies that plague unstructured codebases.</p>
<h3 id="circular-imports">
  Circular Imports
  <a class="heading-link" href="#circular-imports">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Flat structures produce import cycles. A service imports a repository. The repository emits domain events, so it imports the event publisher. The event publisher logs, so it imports the logger. The logger needs configuration, so it imports the config loader. The config loader validates schemas, so it imports domain models from the service. The circle closes, and the build breaks.</p>
<p>Layers break cycles through <strong>unidirectional dependency flow</strong>. Services depend on repositories, but repositories do not depend on services. If a repository needs to publish an event, it accepts an <code>EventPublisher</code> interface via dependency injection, and the entrypoint wires a concrete publisher from the foundation layer. The repository imports <code>interfaces.EventPublisher</code>, not a service package.</p>
<h3 id="god-services">
  God Services
  <a class="heading-link" href="#god-services">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Without clear boundaries, services accumulate responsibilities. A <code>UserService</code> starts with authentication. Then it handles email notifications. Then it manages file uploads. Then it orchestrates payment processing. The service becomes a thousand-line monolith that depends on every repository and client in the system.</p>
<p>Layers enforce the <strong>single responsibility principle</strong> through separation. Authentication belongs in an <code>AuthService</code>. Notifications belong in a <code>NotificationService</code>. File uploads belong in a <code>StorageService</code>. Each service depends on a narrow subset of repositories and clients. Workflows coordinate multi-service operations.</p>
<h3 id="infrastructure-leaking-into-business-logic">
  Infrastructure Leaking into Business Logic
  <a class="heading-link" href="#infrastructure-leaking-into-business-logic">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Services that directly import database drivers, HTTP clients, or message brokers tie business logic to infrastructure choices. Switching from PostgreSQL to DynamoDB requires changing every service method that constructs SQL queries. Replacing Kafka with RabbitMQ requires modifying every event publisher call site.</p>
<p>Repository and client abstractions isolate infrastructure. Services depend on <code>interfaces.Repository[User, UserParams, uuid.UUID]</code>, not <code>*sql.DB</code>. Swapping persistence backends requires only a different repository implementation at the DI layer. Service code remains unchanged.</p>
<h3 id="untestable-code">
  Untestable Code
  <a class="heading-link" href="#untestable-code">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Services that instantiate dependencies internally are hard to test. A service that creates its own database connection requires a real database for tests. A service that calls external APIs directly requires network access or elaborate mocking.</p>
<p>Dependency injection through interfaces makes testing trivial. A service test injects a fake repository:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestService_GetUser</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fakeRepo</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">FakeUserRepository</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Users</span>: <span style="color:#66d9ef">map</span>[<span style="color:#a6e22e">uuid</span>.<span style="color:#a6e22e">UUID</span>]<span style="color:#a6e22e">User</span>{
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">testUserID</span>: {<span style="color:#a6e22e">ID</span>: <span style="color:#a6e22e">testUserID</span>, <span style="color:#a6e22e">Email</span>: <span style="color:#e6db74">&#34;test@example.com&#34;</span>},
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">service</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">NewUserService</span>(<span style="color:#a6e22e">fakeRepo</span>, <span style="color:#a6e22e">fakeLogger</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">user</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">service</span>.<span style="color:#a6e22e">GetUser</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">testUserID</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">require</span>.<span style="color:#a6e22e">NoError</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">Equal</span>(<span style="color:#a6e22e">t</span>, <span style="color:#e6db74">&#34;test@example.com&#34;</span>, <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">Email</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>No database, no network, no external dependencies. Tests run in milliseconds.</p>
<h3 id="deployment-coupling">
  Deployment Coupling
  <a class="heading-link" href="#deployment-coupling">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Microservices that share code through direct imports couple deployment. Service A imports a utility function from Service B&rsquo;s internal package. Deploying Service B requires coordinating with Service A to ensure compatibility. The services are no longer independently deployable.</p>
<p>Layers eliminate coupling through <strong>shared libraries in foundation</strong>. Utilities live in <code>pkg/go/foundation/</code>, not in application packages. Services import foundation modules, not each other. Foundation modules are independently versioned, and services declare version constraints in their <code>go.mod</code> files. Upgrading a foundation module in Service A does not affect Service B until Service B updates its own dependency declaration.</p>
<h2 id="summary">
  Summary
  <a class="heading-link" href="#summary">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The ideal repository structure is not about naming conventions or directory aesthetics. It is about <strong>enforcing dependency direction through physical boundaries</strong>. Layers prevent circular dependencies by making upward imports impossible. Layers prevent God components by separating responsibilities. Layers prevent infrastructure leakage by isolating abstractions from implementations. Layers prevent deployment coupling by limiting shared code to independently versioned foundation modules.</p>
<p>Layers are directories. Interfaces are files in a dedicated package. Dependencies flow downward. Violations fail at compile time or in CI. The structure becomes self-enforcing, and architectural decay stops being inevitable.</p>
<p>The nine-layer model scales from small services to large polyglot monorepos. The Go structure in mothership and the Python structure in hpc demonstrate the same principles with language-appropriate implementations. Both enforce the dependency rule. Both define interfaces before implementations. Both use decorator composition for cross-cutting concerns. Both rely on physical directory structure to make architecture visible and violations obvious.</p>
<p>Structure is not overhead. Structure is the mechanism that prevents complexity from compounding into chaos.</p>
]]></content:encoded><category>architecture</category><category>monorepo</category><category>design-patterns</category><category>go</category><category>python</category></item><item><title>How I Learned to Love the Bomb</title><link>https://alexdjalali.github.io/posts/how-i-learned-to-love-the-bomb/</link><guid isPermaLink="true">https://alexdjalali.github.io/posts/how-i-learned-to-love-the-bomb/</guid><pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate><dc:creator>a.j.djalali</dc:creator><description>A developer's journey from AI skepticism to building an entire engineering system around it. Adapt or die — and what adaptation actually looks like.</description><content:encoded><![CDATA[<p>I spent the better part of two decades building software the old-fashioned way. I read the docs. I typed every line. I had opinions about whitespace. When GitHub Copilot showed up in 2021 and started finishing my sentences, my reaction was roughly the same as a novelist watching someone autocomplete their paragraphs: <em>No thank you.</em></p>
<p>I wasn&rsquo;t alone. The developers I respected most were skeptical. The arguments wrote themselves: it hallucinates, it produces subtly wrong code, it doesn&rsquo;t understand architecture, it makes junior developers worse by letting them skip the part where they actually learn. I repeated these arguments often, and I believed them. Some of them are still true.</p>
<p>But here&rsquo;s what happened: I was wrong about the conclusion.</p>
<h2 id="the-resistance">
  The Resistance
  <a class="heading-link" href="#the-resistance">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>My skepticism wasn&rsquo;t uninformed. I&rsquo;d spent years building natural language systems — first at <a href="https://www.crunchbase.com/organization/cleargraph"  class="external-link" target="_blank" rel="noopener">ClearGraph</a>, then at <a href="https://www.tableau.com/"  class="external-link" target="_blank" rel="noopener">Tableau</a>, then at <a href="https://www.salesforce.com/"  class="external-link" target="_blank" rel="noopener">Salesforce</a>. I understood language models. I knew what they were good at (pattern matching, surface fluency) and what they were bad at (reasoning, consistency, knowing when they don&rsquo;t know something). When people told me GPT-3 was going to replace software engineers, I had the technical background to explain exactly why that was wrong.</p>
<p>And it was wrong — in 2022. The problem is that I let being right about the timeline make me complacent about the trajectory.</p>
<p>While I was explaining why AI-generated code was unreliable, the tools got better. While I was pointing out hallucination rates, context windows grew from 4K to 128K to effectively unlimited. While I was insisting that real engineering required human judgment, a generation of developers started shipping production code with AI assistance and — here&rsquo;s the part that stung — some of them were moving faster than I was.</p>
<h2 id="bill-kennedy">
  Bill Kennedy
  <a class="heading-link" href="#bill-kennedy">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The person who changed my mind was <a href="https://www.ardanlabs.com/about/"  class="external-link" target="_blank" rel="noopener">Bill Kennedy</a>.</p>
<p>Bill runs <a href="https://www.ardanlabs.com/"  class="external-link" target="_blank" rel="noopener">Ardan Labs</a> and has spent years teaching Go to engineers who care about doing it right. He&rsquo;s not a hype guy. He&rsquo;s the opposite — rigorous, opinionated about engineering discipline, allergic to shortcuts. So when Bill started talking seriously about AI-assisted development, I couldn&rsquo;t dismiss it the way I&rsquo;d dismissed the Twitter evangelists. This wasn&rsquo;t someone chasing a trend. This was someone who had looked at the tools, measured the output, and concluded that the leverage was real — <em>if</em> you brought the same discipline to AI-assisted code that you&rsquo;d bring to any other code.</p>
<p>Bill&rsquo;s argument wasn&rsquo;t &ldquo;AI writes code for you.&rdquo; It was closer to: &ldquo;AI is a force multiplier for engineers who already know what good looks like.&rdquo; The standards don&rsquo;t relax. The quality bar doesn&rsquo;t drop. You just move faster at the parts that were never the hard parts to begin with.</p>
<p>That distinction mattered to me. It was the difference between &ldquo;replace your judgment&rdquo; and &ldquo;free up your judgment for the work that actually needs it.&rdquo;</p>
<h2 id="georgia-tech">
  Georgia Tech
  <a class="heading-link" href="#georgia-tech">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>I didn&rsquo;t start using AI tools seriously until I joined <a href="https://www.gatech.edu/"  class="external-link" target="_blank" rel="noopener">Georgia Tech</a> as Research Faculty. Through ClearGraph, Tableau, Salesforce, co-founding <a href="https://www.perceptivepanda.com/"  class="external-link" target="_blank" rel="noopener">PerceptivePanda</a>, the <a href="https://zapier.com/"  class="external-link" target="_blank" rel="noopener">Zapier</a> acquisition — all of that was done the old way. Every line written by hand.</p>
<p>At Georgia Tech, the context shifted. I was working across multiple codebases, multiple languages, moving between research and engineering. The surface area was broader than anything I&rsquo;d dealt with at a single company. Bill&rsquo;s words kept echoing: <em>force multiplier for engineers who already know what good looks like.</em></p>
<p>So I started small. First it was autocomplete. Fine, let Copilot finish the obvious boilerplate. I&rsquo;m not going to type <code>if err != nil { return fmt.Errorf(&quot;failed to parse config: %w&quot;, err) }</code> for the ten thousandth time if a machine will do it for me. That&rsquo;s not intelligence, that&rsquo;s typing.</p>
<p>Then it was exploration. I started asking Claude questions about codebases I was unfamiliar with. Not to write code — just to orient. What does this module do? Where are the entry points? What&rsquo;s the error handling strategy? It turned out that having a conversation with something that had read every Go package on GitHub was genuinely useful for navigating unfamiliar territory.</p>
<p>Then it was drafting. I&rsquo;d describe a function signature, the expected behavior, the edge cases. The model would produce a first draft. I&rsquo;d rewrite 60% of it. Then 40%. Then 20%. The drafts kept getting better, and I kept getting faster.</p>
<p>Within months, AI tools were load-bearing infrastructure in my workflow. The question had shifted from <em>should I use these tools</em> to <em>how do I use them without losing the things that matter</em>.</p>
<h2 id="the-problem-with-vibe-coding">
  The Problem with &ldquo;Vibe Coding&rdquo;
  <a class="heading-link" href="#the-problem-with-vibe-coding">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Here&rsquo;s what I didn&rsquo;t want to become: a prompt jockey. Someone who types &ldquo;build me an app&rdquo; into a chat window and ships whatever comes back. The industry has a word for this now — <em>vibe coding</em> — and I find it genuinely dangerous.</p>
<p>Not because the output is always bad. Sometimes it&rsquo;s remarkably good. The danger is that it divorces the developer from the decisions. When you let a model choose your architecture, your error handling strategy, your test coverage, your dependency graph — you haven&rsquo;t saved time. You&rsquo;ve accumulated technical debt at machine speed. You just don&rsquo;t know it yet, because the code runs and the tests (if there are any) pass.</p>
<p>The developers who are going to thrive with AI are not the ones who prompt the hardest. They&rsquo;re the ones who know what good engineering looks like and refuse to accept anything less, regardless of who — or what — wrote the code.</p>
<h2 id="the-system">
  The System
  <a class="heading-link" href="#the-system">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>So I built a system. Not a product, not a framework — a set of engineering standards that I enforce across every AI tool I use. The same rules, the same quality gates, the same architectural patterns, whether the code is coming from <a href="https://docs.anthropic.com/en/docs/claude-code"  class="external-link" target="_blank" rel="noopener">Claude Code</a>, <a href="https://www.cursor.com/"  class="external-link" target="_blank" rel="noopener">Cursor</a>, or <a href="https://kilocode.ai/"  class="external-link" target="_blank" rel="noopener">Kilo Code</a>.</p>
<p>The core of it is a document I call my global standards. It lives in my dotfiles and gets symlinked into every tool&rsquo;s configuration directory. It specifies:</p>
<p><strong>Process</strong> — every structural change follows a pipeline: ADR first, then architecture diagrams, then decompose into stories, then plan, then implement, then verify. AI doesn&rsquo;t get to skip steps.</p>
<p><strong>Quality gates</strong> — before every commit, four things must pass: formatting, linting, type checking, and unit tests. No exceptions. No &ldquo;I&rsquo;ll fix it later.&rdquo; The AI writes the code, and the code meets the same bar as anything I&rsquo;d write by hand.</p>
<p><strong>Architecture patterns</strong> — clean layered architecture with dependency injection, repository pattern for data access, early returns over nested conditionals. Separate your persistence models from your domain entities from your DTOs. These aren&rsquo;t suggestions. They&rsquo;re constraints.</p>
<p><strong>Anti-patterns</strong> — files over 800 lines, functions over 50 lines, nesting deeper than 4 levels, hardcoded secrets, mutable shared state, <code>any</code> types without narrowing, import cycles. If the AI produces any of these, it gets rejected. Every time.</p>
<p>Each language gets its own standards file. Python means <code>uv</code> for package management, <code>ruff</code> for linting, <code>basedpyright</code> for type checking, <code>pytest</code> with markers per layer, <code>attrs</code> classes for models, Google-style docstrings, and property-based testing with <code>hypothesis</code>. Go means <code>gofumpt</code> for formatting, <code>golangci-lint</code> for linting, table-driven tests with <code>t.Run()</code> subtests, small consumer-defined interfaces, and structured logging with <code>zap</code>. TypeScript means <code>pnpm</code>, strict <code>tsc</code>, <code>vitest</code>, functional React components, and Tailwind utilities — never <code>@apply</code>.</p>
<p>These aren&rsquo;t aspirational documents. They&rsquo;re enforced. When I open Claude Code, it reads my <code>CLAUDE.md</code> and knows the rules before I type a single prompt. When I open Cursor, the <code>.cursorrules</code> file tells it how this specific project works — down to the import paths for the error hierarchy and the structured logging package. When I open Kilo Code, the same standards are waiting in <code>~/.kilocode/rules/</code>.</p>
<p>The result is that I get three different AI tools, built by three different companies, all producing code that looks like <em>my</em> code. Same patterns. Same conventions. Same quality bar.</p>
<h2 id="what-it-actually-looks-like">
  What It Actually Looks Like
  <a class="heading-link" href="#what-it-actually-looks-like">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Here&rsquo;s a typical session. I&rsquo;m adding a new domain entity to a Go service. I open Claude Code and describe what I need:</p>
<blockquote>
<p>Add a <code>workspace</code> domain entity with CRUD operations, Ent repository, and HTTP controller. Follow the existing <code>user</code> domain as the reference implementation.</p>
</blockquote>
<p>Claude Code reads my standards, examines the existing <code>user</code> domain for patterns, and produces a plan. Not code — a plan. It identifies the files it needs to create, the interfaces it needs to implement, the tests it needs to write. I review the plan. I adjust it. Then it executes.</p>
<p>The code that comes out has structured logging with <code>zap</code>, errors wrapped with <code>cockroachdb/errors</code>, context as the first parameter, table-driven tests, the full layered architecture from <code>cmd</code> to <code>controller</code> to <code>core</code> to <code>foundation</code>. Not because the model is brilliant, but because I told it exactly what good looks like and gave it a reference implementation to follow.</p>
<p>When something doesn&rsquo;t meet the bar, I reject it. The model learns from the correction within the session. Over time, my standards files get more precise based on the patterns of mistakes I see. It&rsquo;s a feedback loop: the AI makes me more explicit about what I want, and that explicitness makes the AI more reliable.</p>
<h2 id="tdd-still-matters">
  TDD Still Matters
  <a class="heading-link" href="#tdd-still-matters">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>One of the standards I enforce hardest is TDD. Write the failing test first. Red, green, refactor. Every AI tool I use knows this rule.</p>
<p>This matters more with AI-generated code, not less. When a human writes code, they have a mental model of the invariants. When a model writes code, it has a statistical approximation of what code in this context usually looks like. Tests are the mechanism that bridges that gap. If the model&rsquo;s implementation satisfies the tests I wrote — tests that encode my understanding of the requirements — then I have confidence in the output regardless of whether a human or a machine produced it.</p>
<p>The developers who skip tests because &ldquo;the AI wrote it and it looks right&rdquo; are going to learn expensive lessons.</p>
<h2 id="the-uncomfortable-truth">
  The Uncomfortable Truth
  <a class="heading-link" href="#the-uncomfortable-truth">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Here&rsquo;s the part that&rsquo;s hard to say: I&rsquo;m faster now. Meaningfully faster. Not at the parts of programming that matter most — design, architecture, understanding the problem — but at the mechanical translation of decisions into working code. The part where you know exactly what you want and you need to produce 400 lines of well-structured, well-tested, well-documented implementation. That part used to take an afternoon. Now it takes twenty minutes.</p>
<p>This means one of two things. Either the mechanical translation was never where my value as an engineer lived, or I was spending a lot of my career on work that didn&rsquo;t require my full attention. Probably both.</p>
<p>The developers who are threatened by this are the ones whose primary skill is typing speed and syntax recall. The developers who are amplified by it are the ones whose primary skill is judgment: knowing what to build, how to structure it, what trade-offs to accept, and — critically — knowing when the AI&rsquo;s output is wrong.</p>
<h2 id="what-i-got-wrong">
  What I Got Wrong
  <a class="heading-link" href="#what-i-got-wrong">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>I got two things wrong about AI and software engineering.</p>
<p>First, I thought the quality floor was fixed. I assumed that AI-generated code would always be mediocre and that serious engineers would always need to rewrite it. What I didn&rsquo;t anticipate was that the quality floor is a function of the constraints you impose. Unconstrained, yes, AI writes mediocre code. But with 70 pages of standards, enforced quality gates, reference implementations, and a human who knows what good looks like? The floor rises dramatically.</p>
<p>Second, I thought adoption was binary. Either you&rsquo;re a &ldquo;real&rdquo; developer who writes everything by hand, or you&rsquo;re a prompt engineer who ships slop. The reality is a spectrum, and the interesting part of that spectrum is where human judgment and AI capability overlap. That&rsquo;s where the leverage is.</p>
<h2 id="adapt-or-die">
  Adapt or Die
  <a class="heading-link" href="#adapt-or-die">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The tools exist. They&rsquo;re getting better. The developers who refuse to engage with them aren&rsquo;t preserving craftsmanship — they&rsquo;re ceding ground to developers who use AI <em>and</em> have good judgment.</p>
<p>The answer isn&rsquo;t to abandon your standards. It&rsquo;s to encode them. Write them down. Make them machine-readable. Enforce them automatically. Then use every tool available to you — AI included — to ship better software faster, without compromising on the things that actually matter.</p>
<p>Bill was right. The leverage is real. But only if you bring the discipline with you.</p>
<p>I still have opinions about whitespace. I still believe in TDD. I still think architecture matters more than velocity. I just let a machine handle the typing.</p>
]]></content:encoded><category>ai</category><category>software-engineering</category><category>developer-tools</category><category>claude</category><category>cursor</category></item><item><title>The Decorator Pattern for Observability and Resilience</title><link>https://alexdjalali.github.io/posts/decorator-pattern-observability-resilience/</link><guid isPermaLink="true">https://alexdjalali.github.io/posts/decorator-pattern-observability-resilience/</guid><pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate><dc:creator>a.j.djalali</dc:creator><description>How the decorator pattern cleanly separates logging, metrics, retry, and circuit breaking from business logic — with examples in Python and Go.</description><content:encoded><![CDATA[<p>Production services require logging, metrics collection, retry logic, and circuit breaking. The central question is where these cross-cutting concerns should reside. The naive approach — scattering observability and resilience logic directly inside business methods — produces implementations that are predominantly infrastructure boilerplate, with the actual domain logic obscured. Modifications to retry policies or metrics backends require changes across every service in the codebase, violating the single-responsibility principle and making the system brittle to operational change.</p>
<p>The decorator pattern provides a principled alternative. Each cross-cutting concern is extracted into a standalone wrapper that implements the same interface as the component it decorates. Each wrapper adds exactly one behavior — logging, metrics, retry, or circuit breaking — then delegates to the inner implementation. The business logic remains unaware that it is being observed or protected, and each concern can be developed, tested, and modified independently.</p>
<h2 id="motivation-failure-in-distributed-systems">
  Motivation: Failure in Distributed Systems
  <a class="heading-link" href="#motivation-failure-in-distributed-systems">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>In a monolithic system, a failed function call raises an exception that the caller handles directly. Distributed systems present a fundamentally different failure model. Network partitions, request timeouts, overloaded upstreams, and cold starts are not edge cases; they are routine operational conditions. Every service-to-service call requires a defensive layer: retries with exponential backoff to allow transient failures to resolve, circuit breakers to prevent a degraded upstream from cascading into the caller, timeouts to bound resource consumption on hung connections, and structured logging to enable distributed tracing across service boundaries.</p>
<p>The decorator pattern enables uniform application of these concerns across every client and service boundary without duplicating the implementation. A single <code>RetryDecorator</code> wraps any client interface. A single <code>CircuitBreakerDecorator</code> protects any upstream dependency. When the operational team determines that a backoff strategy should change from exponential to jittered, the modification is confined to one file. When a new database client is introduced, the same decorator stack is composed with a domain-specific error classifier, and the full set of protections — logging, metrics, retry, circuit breaking — is applied automatically.</p>
<h2 id="the-pattern-in-go">
  The Pattern in Go
  <a class="heading-link" href="#the-pattern-in-go">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Go&rsquo;s implicit interface satisfaction makes the decorator pattern particularly natural. A logging decorator for an event producer is a struct that holds the inner producer and a logger. It satisfies the same <code>EventProducer</code> interface, so callers cannot distinguish it from the underlying implementation:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// loggingProducer wraps an EventProducer with structured logging.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// It records publish duration, event name, and any errors without</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// modifying the underlying producer&#39;s behavior.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">loggingProducer</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">inner</span>  <span style="color:#a6e22e">EventProducer</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">logger</span> <span style="color:#a6e22e">Logger</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// WithLogging decorates a producer with entry/exit logging.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Returns the same EventProducer interface — callers cannot tell</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// they are talking to a decorator.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">WithLogging</span>(<span style="color:#a6e22e">p</span> <span style="color:#a6e22e">EventProducer</span>, <span style="color:#a6e22e">logger</span> <span style="color:#a6e22e">Logger</span>) <span style="color:#a6e22e">EventProducer</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">loggingProducer</span>{<span style="color:#a6e22e">inner</span>: <span style="color:#a6e22e">p</span>, <span style="color:#a6e22e">logger</span>: <span style="color:#a6e22e">logger</span>}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">p</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">loggingProducer</span>) <span style="color:#a6e22e">Publish</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">event</span> <span style="color:#a6e22e">Event</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">start</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">inner</span>.<span style="color:#a6e22e">Publish</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">event</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">logger</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;publish&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;event&#34;</span>, <span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">Name</span>(),
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;duration&#34;</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Since</span>(<span style="color:#a6e22e">start</span>),
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;error&#34;</span>, <span style="color:#a6e22e">err</span>,
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>A circuit breaker decorator follows the same structural pattern. It checks the circuit state before each call and records the outcome afterward. If the upstream has exceeded the failure threshold, the decorator short-circuits immediately without attempting the call:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// circuitBreakerProducer fails fast when the upstream is degraded.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// BeforeCall checks the circuit state; AfterCall records the outcome</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// so the breaker can track the failure rate over time.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">circuitBreakerProducer</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">inner</span> <span style="color:#a6e22e">EventProducer</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">cb</span>    <span style="color:#a6e22e">CircuitBreaker</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">p</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">circuitBreakerProducer</span>) <span style="color:#a6e22e">Publish</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">event</span> <span style="color:#a6e22e">Event</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">cb</span>.<span style="color:#a6e22e">BeforeCall</span>(<span style="color:#a6e22e">ctx</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span> <span style="color:#75715e">// circuit is open — fail fast</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">inner</span>.<span style="color:#a6e22e">Publish</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">event</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">cb</span>.<span style="color:#a6e22e">AfterCall</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The essential property is that every decorator accepts and returns the same interface. This makes decorators composable — an arbitrary number can be stacked without altering any type signatures.</p>
<h2 id="the-pattern-in-python">
  The Pattern in Python
  <a class="heading-link" href="#the-pattern-in-python">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Python&rsquo;s <code>__getattr__</code> proxy mechanism extends the pattern further. Rather than wrapping each method individually, the decorator intercepts attribute access at the class level, determines whether the attribute is callable, and wraps it transparently. This approach yields a single decorator class that operates on any interface without per-method boilerplate:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">LoggingDecorator</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;Transparent proxy that logs entry, exit, and errors for every call.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    Uses __getattr__ so it works with any interface — no per-method
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    boilerplate. Adding logging to a new client is a single line:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        client = LoggingDecorator(raw_client, service=&#34;neo4j&#34;, logger=logger)
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    &#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">__init__</span>(self, inner, <span style="color:#f92672">*</span>, service: str, logger):
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_inner <span style="color:#f92672">=</span> inner
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_logger <span style="color:#f92672">=</span> logger
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>_service <span style="color:#f92672">=</span> service
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">__getattr__</span>(self, name):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Delegate non-callable attributes unchanged</span>
</span></span><span style="display:flex;"><span>        attr <span style="color:#f92672">=</span> getattr(self<span style="color:#f92672">.</span>_inner, name)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> callable(attr):
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> attr
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">@wraps</span>(attr)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">wrapper</span>(<span style="color:#f92672">*</span>args, <span style="color:#f92672">**</span>kwargs):
</span></span><span style="display:flex;"><span>            start <span style="color:#f92672">=</span> time<span style="color:#f92672">.</span>monotonic()
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">try</span>:
</span></span><span style="display:flex;"><span>                result <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> attr(<span style="color:#f92672">*</span>args, <span style="color:#f92672">**</span>kwargs)
</span></span><span style="display:flex;"><span>                self<span style="color:#f92672">.</span>_logger<span style="color:#f92672">.</span>debug(
</span></span><span style="display:flex;"><span>                    <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">%s</span><span style="color:#e6db74">.</span><span style="color:#e6db74">%s</span><span style="color:#e6db74"> ok&#34;</span>,
</span></span><span style="display:flex;"><span>                    self<span style="color:#f92672">.</span>_service, name,
</span></span><span style="display:flex;"><span>                    extra<span style="color:#f92672">=</span>{<span style="color:#e6db74">&#34;duration_ms&#34;</span>: (time<span style="color:#f92672">.</span>monotonic() <span style="color:#f92672">-</span> start) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1000</span>},
</span></span><span style="display:flex;"><span>                )
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">return</span> result
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">except</span> <span style="color:#a6e22e">Exception</span> <span style="color:#66d9ef">as</span> exc:
</span></span><span style="display:flex;"><span>                self<span style="color:#f92672">.</span>_logger<span style="color:#f92672">.</span>warning(
</span></span><span style="display:flex;"><span>                    <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">%s</span><span style="color:#e6db74">.</span><span style="color:#e6db74">%s</span><span style="color:#e6db74"> failed&#34;</span>,
</span></span><span style="display:flex;"><span>                    self<span style="color:#f92672">.</span>_service, name,
</span></span><span style="display:flex;"><span>                    extra<span style="color:#f92672">=</span>{<span style="color:#e6db74">&#34;error&#34;</span>: str(exc)},
</span></span><span style="display:flex;"><span>                )
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">raise</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> wrapper
</span></span></code></pre></div><p>The same <code>__getattr__</code> approach generalizes to metrics, retry, timeout, and circuit breaker decorators. Each is a standalone class capable of wrapping any object that exposes callable attributes.</p>
<h2 id="composition-stacking-decorators">
  Composition: Stacking Decorators
  <a class="heading-link" href="#composition-stacking-decorators">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The power of the pattern lies in composition. Each decorator is independent — it is aware only of the interface it wraps and the single concern it introduces. Decorators are composed into a stack where each layer contributes exactly one behavior. A fluent builder makes the ordering explicit and the composition readable:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Fluent builder — reads top-to-bottom as outermost to innermost.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// A call enters at Recovery, flows through Auth → Transaction →</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Observability, reaches the concrete service, then unwinds back out.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">svc</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">decorators</span>.<span style="color:#a6e22e">NewBuilder</span>(<span style="color:#a6e22e">NewUserService</span>(<span style="color:#a6e22e">repo</span>), <span style="color:#e6db74">&#34;users&#34;</span>).
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">WithRecovery</span>().
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">WithAuthorization</span>(<span style="color:#a6e22e">authorizer</span>).
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">WithTransaction</span>(<span style="color:#a6e22e">txManager</span>).
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">WithObservability</span>(<span style="color:#a6e22e">logger</span>, <span style="color:#a6e22e">metrics</span>, <span style="color:#a6e22e">tracer</span>).
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Build</span>()
</span></span></code></pre></div><p>Without a builder, the manual composition is equally explicit. Each variable name documents the layer being applied:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># Stack order: Logging → Metrics → Retry → CircuitBreaker → Timeout → Raw</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Each decorator wraps the previous, adding one concern.</span>
</span></span><span style="display:flex;"><span>raw <span style="color:#f92672">=</span> Neo4JClient(uri<span style="color:#f92672">=</span>uri, user<span style="color:#f92672">=</span>user, password<span style="color:#f92672">=</span>password)
</span></span><span style="display:flex;"><span>with_timeout <span style="color:#f92672">=</span> TimeoutDecorator(raw, timeout_seconds<span style="color:#f92672">=</span><span style="color:#ae81ff">30.0</span>, service<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;neo4j&#34;</span>)
</span></span><span style="display:flex;"><span>with_cb <span style="color:#f92672">=</span> CircuitBreakerDecorator(with_timeout, name<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;neo4j&#34;</span>, threshold<span style="color:#f92672">=</span><span style="color:#ae81ff">5</span>)
</span></span><span style="display:flex;"><span>with_retry <span style="color:#f92672">=</span> RetryDecorator(with_cb, max_attempts<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span>, classifier<span style="color:#f92672">=</span>Neo4JErrorClassifier())
</span></span><span style="display:flex;"><span>with_metrics <span style="color:#f92672">=</span> MetricsDecorator(with_retry, service<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;neo4j&#34;</span>)
</span></span><span style="display:flex;"><span>client <span style="color:#f92672">=</span> LoggingDecorator(with_metrics, service_name<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;neo4j&#34;</span>, logger<span style="color:#f92672">=</span>logger)
</span></span></code></pre></div><p>When a new database client is introduced — Redis, Elasticsearch, Kafka — the same decorator stack is composed with a domain-specific error classifier, and the full complement of observability and resilience behaviors is inherited without additional implementation effort.</p>
<h2 id="summary">
  Summary
  <a class="heading-link" href="#summary">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The decorator pattern confines each cross-cutting concern to a single file with a single responsibility. Business logic remains clean: a repository implementation is concerned with data access, not with Prometheus histograms or retry scheduling. Decorators are individually testable — the inner interface is mocked, and the wrapper&rsquo;s behavior is verified in isolation. The composition is explicit and inspectable: the builder chain or factory function specifies exactly which protections are in place and in what order. There is no annotation indirection, no framework-level interception, and no implicit behavior — only functions wrapping functions, each with a well-defined contract.</p>
]]></content:encoded><category>architecture</category><category>design-patterns</category><category>observability</category><category>resilience</category></item><item><title>From Dependency Parses to Montague Semantics via Vector-Grounded Concept Bases</title><link>https://alexdjalali.github.io/posts/dependency-to-lambda/</link><guid isPermaLink="true">https://alexdjalali.github.io/posts/dependency-to-lambda/</guid><pubDate>Sat, 31 Jan 2026 00:00:00 +0000</pubDate><dc:creator>a.j.djalali</dc:creator><description>Exploring how Universal Dependencies parse trees, Montague-style typed lambda calculus, and vector-derived concept bases fit together into a compositional semantics pipeline.</description><content:encoded><![CDATA[<p>A persistent challenge in computational semantics is bridging the gap between syntactic parse representations — which capture grammatical structure — and formal semantic representations capable of supporting inference. Dependency parsers produce trees annotated with grammatical relations (subject, object, modifier), but these structures lack the compositional semantics needed for entailment, quantifier scope resolution, or logical reasoning. This post explores how three well-established traditions — Universal Dependencies parsing, <a href="#ref-montague1973">Montague</a>-style higher-order typed lambda calculus, and distributional vector semantics — can be synthesized into a coherent pipeline that translates dependency trees into concept-grounded logical forms. The discussion draws on foundational work by <a href="#ref-reddy2016">Reddy et al. (2016)</a> on transforming dependency structures to logical forms, as well as recent syntax-guided approaches to semantic translation <a href="#ref-bai2021">(Bai &amp; Zhao, 2021)</a>.</p>
<h2 id="architecture">
  Architecture
  <a class="heading-link" href="#architecture">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>A natural way to organize these components is as a multi-stage pipeline. A <a href="https://stanfordnlp.github.io/CoreNLP/"  class="external-link" target="_blank" rel="noopener">Stanford CoreNLP</a> dependency parser <a href="#ref-chen2014">(Chen &amp; Manning, 2014)</a> produces token-level annotations — lemma, POS tag, dependency relation, and head position — which feed into two parallel tracks. The Stanford parser generates Universal Dependencies (UD) trees <a href="#ref-demarneffe2014">(de Marneffe et al., 2014)</a> via its neural network dependency parser, providing typed dependency relations (e.g., <code>nsubj</code>, <code>obj</code>, <code>det</code>, <code>amod</code>) that serve as the syntactic backbone for semantic translation.</p>
<p>The first track embeds tokens via sentence transformers and clusters the resulting vectors into canonical concepts using deterministic k-means. The second track extracts <a href="#ref-dowty1991">Dowty (1991)</a> proto-role features from dependency arcs and classifies verbs according to <a href="#ref-levin1993">Beth Levin&rsquo;s (1993)</a> taxonomy. Both tracks converge in a semantic graph builder, which constructs a typed predicate-argument structure. A final generation stage produces a lambda-calculus term from the graph, applies beta-reduction and simplification, validates types, and emits a canonical string representation. None of these components are individually novel — the contribution is in tracing how they compose.</p>
<p>As a running illustration, the sentence <em>&ldquo;Every tall senator approved the new bill&rdquo;</em> enters the pipeline as raw text and exits as a fully typed, quantifier-scoped lambda term — with each lexical constant grounded to a canonical concept ID derived from its embedding cluster. The remainder of this post traces that sentence through each stage.</p>
<h2 id="the-type-system">
  The Type System
  <a class="heading-link" href="#the-type-system">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The standard representation language for this kind of pipeline is a higher-order typed lambda calculus in the <a href="#ref-montague1973">Montague (1973)</a> tradition. The type system comprises two primitive types — $e$ (entity) and $t$ (truth value) — and a recursive function type constructor. Common derived types include properties ($e \to t$), binary relations ($e \to (e \to t)$), generalized quantifiers ($(e \to t) \to ((e \to t) \to t)$), and connectives ($t \to (t \to t)$).</p>
<p>The primitive and derived types are defined as follows:</p>
<pre tabindex="0"><code>Primitive types:
    e                                       // individuals
    t                                       // propositions

Derived types:
    Property    = e → t
    BinaryRel   = e → (e → t)
    TernaryRel  = e → (e → (e → t))
    Determiner  = (e → t) → ((e → t) → t)
    Connective  = t → (t → t)
    Modifier    = (e → t) → (e → t)
</code></pre><p>The type system constrains well-formedness: a function of type $e \to t$ can only be applied to a term of type $e$, and the result has type $t$. Attempting to apply a property to another property — for example, $(\textsf{Tall}\;\textsf{Senator})$ where both are $e \to t$ — raises a type error at construction time. This enables type-driven composition and catches arity mismatches before evaluation.</p>
<p>For example, the determiner <em>every</em> has type $(e \to t) \to ((e \to t) \to t)$. It consumes a restrictor property (the noun) and a scope property (the verb phrase), yielding a truth value. The adjective <em>tall</em> has the modifier type $(e \to t) \to (e \to t)$: it takes a property and returns a refined property. These types determine how constituents compose — <em>tall senator</em> is $(\textsf{Tall}\;\textsf{Senator})$ where $\textsf{Tall} : (e \to t) \to (e \to t)$ applied to $\textsf{Senator} : e \to t$ yields a new property of type $e \to t$.</p>
<h2 id="the-ast">
  The AST
  <a class="heading-link" href="#the-ast">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>In Montague&rsquo;s framework, the abstract syntax tree requires exactly four node types. Predicates, quantifiers, and connectives are not distinct AST categories — they are typed constants composed via curried application. The four node types are:</p>
<pre tabindex="0"><code>Variable(x, τ)         // bound or free variable of type τ
Constant(c, τ)         // typed constant (predicate, quantifier, connective)
Application(f, a)      // function application: f(a)
Abstraction(x, φ)      // lambda abstraction: λx. φ
</code></pre><p>Curried application is defined recursively: $\textsf{Apply}(f, a, b) = \textsf{Application}(\textsf{Application}(f, a), b)$.</p>
<p>To illustrate the uniformity of this representation, consider how the words in <em>&ldquo;Every tall senator approved the new bill&rdquo;</em> reduce to the same AST primitives:</p>
<pre tabindex="0"><code>Senator : e → t                             // common noun
Approve : e → (e → t)                       // transitive verb
Bill    : e → t                             // common noun
Every   : (e → t) → ((e → t) → t)          // determiner
The     : (e → t) → ((e → t) → t)          // determiner
Tall    : (e → t) → (e → t)                // modifier
New     : (e → t) → (e → t)                // modifier
</code></pre><p>The phrase <em>&ldquo;senator approved&rdquo;</em> is simply $\textsf{Application}(\textsf{Application}(\textsf{Approve}, \textsf{Senator}), \ldots)$ — no special syntax is required for any linguistic category. Every word in the sentence is a typed constant; composition is handled entirely by $\textsf{Application}$ and $\textsf{Abstraction}$.</p>
<h2 id="translation-dependency-arcs-to-lambda-terms">
  Translation: Dependency Arcs to Lambda Terms
  <a class="heading-link" href="#translation-dependency-arcs-to-lambda-terms">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The central question is how dependency relations map to semantic argument positions. The natural approach, following <a href="#ref-reddy2016">Reddy et al. (2016)</a>, is a recursive transpiler that walks the dependency tree bottom-up, building typed lambda terms at each node. The recursive structure of the algorithm mirrors the recursive structure of the dependency tree itself: each subtree is translated independently, and the results are composed according to the dependency relation that connects them.</p>
<h3 id="recursive-definition">
  Recursive Definition
  <a class="heading-link" href="#recursive-definition">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>The translation function $\mathcal{T}$ is defined over dependency tree nodes. Given a node $n$ with head word $w$, POS tag $p$, and a set of dependents $\{d_1, d_2, \ldots\}$ each bearing a dependency relation $\text{rel}(d_i)$, the function proceeds as follows:</p>
<pre tabindex="0"><code>function T(node) → LambdaTerm

    // Base case: look up the canonical concept and assign a type from POS tag
    term ← Constant(name: concept(node), type: type_for(node.pos))

    // Recursive case: process each dependent by relation type
    for each dep in node.dependents do
        case dep.relation of

            &#34;amod&#34;, &#34;advmod&#34;:
                // Modifiers have type (e→t) → (e→t)
                modifier ← T(dep)
                term ← Application(modifier, term)

            &#34;det&#34;:
                // Determiners have type (e→t) → ((e→t) → t)
                quantifier ← T(dep)
                term ← QuantifierTerm(quantifier, restrictor: term)

            &#34;nsubj&#34;, &#34;csubj&#34;:
                // Subject: recurse into subject subtree, bind as ARG0
                subject ← T(dep)
                term ← bind_argument(term, arg: subject, position: ARG0)

            &#34;obj&#34;, &#34;dobj&#34;:
                // Object: recurse into object subtree, bind as ARG1
                object ← T(dep)
                term ← bind_argument(term, arg: object, position: ARG1)

    return term
</code></pre><p>The $\text{bind\_argument}$ function handles the interaction between quantified noun phrases and the verb. When the argument is a quantifier term (produced by a <code>det</code> dependent), the verb&rsquo;s lambda term is threaded into the quantifier&rsquo;s scope position. When the argument is a bare entity constant (a proper noun), it is applied directly via curried application.</p>
<h3 id="worked-example">
  Worked Example
  <a class="heading-link" href="#worked-example">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Consider the sentence <em>&ldquo;Every tall senator approved the new bill.&rdquo;</em> The Stanford CoreNLP dependency parse <a href="#ref-chen2014">(Chen &amp; Manning, 2014)</a> produces the following tree:</p>
<pre tabindex="0"><code>approved(ROOT)
├── senator(nsubj)
│   ├── every(det)
│   └── tall(amod)
└── bill(obj)
    ├── the(det)
    └── new(amod)
</code></pre><p>The transpiler processes this tree bottom-up, starting from the leaves and composing terms at each level.</p>
<p><strong>Step 1: Translate the subject subtree <code>senator(nsubj)</code>.</strong></p>
<p>The transpiler recurses into the <code>senator</code> node, which has two dependents: <code>tall(amod)</code> and <code>every(det)</code>.</p>
<p>First, the leaf <em>tall</em> is translated to a modifier constant $\textsf{Tall} : (e \to t) \to (e \to t)$. The base noun <em>senator</em> becomes $\textsf{Senator} : e \to t$. The modifier is applied to produce a refined property:</p>
<pre tabindex="0"><code>(Tall Senator) : e → t
</code></pre><p>Next, the determiner <em>every</em> is translated to $\textsf{Every} : (e \to t) \to ((e \to t) \to t)$. The modified noun is wrapped as the restrictor, producing a quantifier term that awaits a scope:</p>
<pre tabindex="0"><code>Every (λx. (Tall Senator) x)  [scope: _]
</code></pre><p><strong>Step 2: Translate the object subtree <code>bill(obj)</code>.</strong></p>
<p>The same recursive procedure applies. The leaf <em>new</em> becomes $\textsf{New} : (e \to t) \to (e \to t)$, and <em>bill</em> becomes $\textsf{Bill} : e \to t$. The modifier applies:</p>
<pre tabindex="0"><code>(New Bill) : e → t
</code></pre><p>The determiner <em>the</em> becomes $\textsf{The} : (e \to t) \to ((e \to t) \to t)$, and the modified noun is wrapped:</p>
<pre tabindex="0"><code>The (λy. (New Bill) y)  [scope: _]
</code></pre><p><strong>Step 3: Compose at the root <code>approved(ROOT)</code>.</strong></p>
<p>The verb <em>approved</em> is assigned the binary relation type $\textsf{Approve} : e \to (e \to t)$. The transpiler now binds the subject and object arguments. The object quantifier&rsquo;s scope is filled first — it receives the verb applied to the subject variable and the object variable:</p>
<pre tabindex="0"><code>Every (λx. (Tall Senator) x)
      (λx. The (λy. (New Bill) y)
               (λy. Approve x y))
</code></pre><p>The outermost quantifier $\textsf{Every}$ takes two arguments: the restrictor $\lambda x.\;(\textsf{Tall}\;\textsf{Senator})\;x$ and the scope $\lambda x.\;\ldots$. Within the scope, the inner quantifier $\textsf{The}$ takes its own restrictor $\lambda y.\;(\textsf{New}\;\textsf{Bill})\;y$ and scope $\lambda y.\;\textsf{Approve}\;x\;y$, where $x$ is bound by the outer abstraction and $y$ by the inner one.</p>
<p><strong>Step 4: The final AST.</strong></p>
<pre tabindex="0"><code>Apply(
    Every,
    Abstraction(x,
        Apply(Application(Tall, Senator), x)),
    Abstraction(x,
        Apply(
            The,
            Abstraction(y,
                Apply(Application(New, Bill), y)),
            Abstraction(y,
                Apply(Approve, x, y)))))
</code></pre><p>Each recursive call to $\mathcal{T}$ produces a self-contained typed term. The composition at each level is determined entirely by the dependency relation — <code>amod</code> triggers modifier application, <code>det</code> triggers quantifier wrapping, <code>nsubj</code> and <code>obj</code> trigger argument binding — which is why the algorithm generalizes across arbitrary dependency trees without sentence-specific rules.</p>
<h2 id="vector-grounded-concept-base">
  Vector-Grounded Concept Base
  <a class="heading-link" href="#vector-grounded-concept-base">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Lexical grounding is the bridge between surface forms and the formal representation. Each token is embedded via a sentence transformer, and the resulting vectors are clustered using seeded k-means into canonical concepts. The seed ensures determinism: identical inputs always produce identical concept assignments. Each cluster centroid defines a canonical concept with a stable SHA-256-derived identifier.</p>
<pre tabindex="0"><code>mapper ← CanonicalConceptMapper(n_clusters: 256, seed: 42)
mapper.fit(feature_vectors)

mapping ← mapper.map(feature, text: &#34;senator&#34;, lemma: &#34;senator&#34;)
// → ConceptMapping(concept_id: &#34;a3f2c8&#34;, label: &#34;Senator&#34;, confidence: 0.87)
</code></pre><p>The concept mapper assigns each token a canonical concept ID and a confidence score derived from the distance to the cluster centroid. These concept IDs are threaded into the lambda-calculus constants via the <code>concept_id</code> field, linking the formal representation to the distributional semantics.</p>
<h3 id="sample-lexicon">
  Sample Lexicon
  <a class="heading-link" href="#sample-lexicon">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>After fitting the concept mapper to a corpus, each cluster centroid defines a canonical concept. The resulting lexicon annotates each concept with its semantic type, cluster ID, representative surface forms, and the proto-role frame assigned by the verb classifier. A fragment of such a lexicon:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Concept ID</th>
          <th style="text-align: left">Label</th>
          <th style="text-align: left">Type</th>
          <th style="text-align: left">Surface Forms</th>
          <th style="text-align: left">Proto-Roles</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><code>a3f2c8</code></td>
          <td style="text-align: left">$\textsf{Senator}$</td>
          <td style="text-align: left">$e \to t$</td>
          <td style="text-align: left">senator, legislator, lawmaker, congressperson</td>
          <td style="text-align: left">—</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>b71e04</code></td>
          <td style="text-align: left">$\textsf{Approve}$</td>
          <td style="text-align: left">$e \to (e \to t)$</td>
          <td style="text-align: left">approve, endorse, ratify, sanction</td>
          <td style="text-align: left">ARG0: Agent, ARG1: Theme</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>c9d431</code></td>
          <td style="text-align: left">$\textsf{Bill}$</td>
          <td style="text-align: left">$e \to t$</td>
          <td style="text-align: left">bill, legislation, measure, statute</td>
          <td style="text-align: left">—</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>d5a1f7</code></td>
          <td style="text-align: left">$\textsf{Tall}$</td>
          <td style="text-align: left">$(e \to t) \to (e \to t)$</td>
          <td style="text-align: left">tall, towering, lofty</td>
          <td style="text-align: left">—</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>e82b90</code></td>
          <td style="text-align: left">$\textsf{Give}$</td>
          <td style="text-align: left">$e \to (e \to (e \to t))$</td>
          <td style="text-align: left">give, hand, pass, deliver</td>
          <td style="text-align: left">ARG0: Agent, ARG1: Recipient, ARG2: Theme</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>f44c12</code></td>
          <td style="text-align: left">$\textsf{Bank}_\text{fin}$</td>
          <td style="text-align: left">$e \to t$</td>
          <td style="text-align: left">bank, lender, financial institution</td>
          <td style="text-align: left">—</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>f44c13</code></td>
          <td style="text-align: left">$\textsf{Bank}_\text{geo}$</td>
          <td style="text-align: left">$e \to t$</td>
          <td style="text-align: left">bank, shore, riverbank, embankment</td>
          <td style="text-align: left">—</td>
      </tr>
  </tbody>
</table>
<p>Each entry records the mapping from distributional cluster to formal type. The surface form sets show which tokens ground to the same concept — <em>senator</em>, <em>legislator</em>, <em>lawmaker</em>, and <em>congressperson</em> all resolve to concept <code>a3f2c8</code> with type $e \to t$. Verbs carry proto-role annotations that inform argument binding during transpilation. The two $\textsf{Bank}$ entries illustrate polysemy resolution: the surface form <em>bank</em> appears in both concepts, but the sentence transformer&rsquo;s contextual embedding disambiguates the intended sense at runtime.</p>
<p>When the transpiler encounters the token <em>ratify</em> in a dependency tree, it queries the lexicon and retrieves concept <code>b71e04</code> ($\textsf{Approve}$), producing the constant $\textsf{Constant}(\text{Approve},\; e \to (e \to t),\; \texttt{b71e04})$. This means the sentences <em>&ldquo;The senator approved the bill&rdquo;</em> and <em>&ldquo;The legislator ratified the measure&rdquo;</em> produce structurally identical lambda terms — both ground to the same concept constants — despite sharing no surface forms.</p>
<p>This grounding enables the recognition that two different surface forms may map to the same canonical concept if their embeddings cluster together. The confidence scores assigned by the mapper decrease with distance from the cluster centroid, reflecting decreasing typicality:</p>
<pre tabindex="0"><code>map(&#34;senator&#34;,    lemma: &#34;senator&#34;)    → (concept_id: &#34;a3f2c8&#34;, label: &#34;Senator&#34;, confidence: 0.87)
map(&#34;legislator&#34;, lemma: &#34;legislator&#34;) → (concept_id: &#34;a3f2c8&#34;, label: &#34;Senator&#34;, confidence: 0.81)
map(&#34;lawmaker&#34;,   lemma: &#34;lawmaker&#34;)   → (concept_id: &#34;a3f2c8&#34;, label: &#34;Senator&#34;, confidence: 0.74)
</code></pre><p>Conversely, polysemous forms are distinguished by context. The word <em>bank</em> in <em>&ldquo;the river bank&rdquo;</em> and <em>&ldquo;the investment bank&rdquo;</em> receives different embeddings from the sentence transformer (which encodes the full sentential context), and these embeddings cluster into distinct canonical concepts — <code>f44c12</code> (financial) vs. <code>f44c13</code> (geographical) — with the contextual embedding resolving the ambiguity before the token reaches the transpiler.</p>
<h2 id="semantic-role-assignment">
  Semantic Role Assignment
  <a class="heading-link" href="#semantic-role-assignment">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>A further refinement involves extracting <a href="#ref-dowty1991">Dowty (1991)</a> proto-role features from dependency arcs to inform argument labeling. Agent-like properties (volitionality, sentience, causation) are associated with ARG0 positions, while patient-like properties (change of state, affectedness) are associated with ARG1.</p>
<p>Verb classification follows <a href="#ref-levin1993">Levin&rsquo;s (1993)</a> taxonomy, which groups verbs by shared syntactic behavior and assigns thematic role frames accordingly. Returning to the running example, the verb <em>approved</em> belongs to a Levin class that shares the frame <code>ARG0:Agent, ARG1:Theme</code> with verbs like <em>endorse</em>, <em>ratify</em>, and <em>sanction</em> — the same synonyms that cluster together in the concept base.</p>
<p>The Stanford parser <a href="#ref-chen2014">(Chen &amp; Manning, 2014)</a> produces $\text{nsubj}(\textit{approved},\;\textit{senator})$ and $\text{obj}(\textit{approved},\;\textit{bill})$. The Dowty classifier examines the proto-role features of each argument: <em>senator</em> is volitional and sentient (proto-agent), and <em>bill</em> undergoes a change of state — it becomes approved (proto-patient). These features confirm the ARG0/ARG1 assignment derived from the dependency relations, providing a redundant check that strengthens confidence in the resulting predicate-argument structure. The final lambda term thus carries both its formal type structure and its grounded proto-role annotations: $\textsf{Approve}$ is labeled with <code>ARG0:Agent, ARG1:Theme</code>, matching the lexicon entry for concept <code>b71e04</code>.</p>
<h2 id="summary">
  Summary
  <a class="heading-link" href="#summary">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The ideas presented here connect three traditions: dependency grammar for syntactic analysis, <a href="#ref-montague1973">Montague</a> semantics for compositional meaning representation, and distributional semantics for lexical grounding. The Stanford CoreNLP dependency parser <a href="#ref-demarneffe2014">(de Marneffe et al., 2014)</a> provides the syntactic backbone — typed dependency relations that a transpiler can map systematically to predicate-argument structures. The typed lambda calculus provides a representation language that supports beta-reduction, type checking, and logical inference. And vector-grounded concept bases bridge surface variation with semantic identity. The synthesis yields a pipeline that takes raw text and produces typed, normalized, concept-grounded logical forms suitable for downstream reasoning tasks. Related approaches include incremental dependency-to-semantics translation for embodied agents <a href="#ref-brick2007">(Brick &amp; Scheutz, 2007)</a>, CCG-based Montague parsing <a href="#ref-upshotmontague">(upshot-montague)</a>, Minimal Recursion Semantics as an alternative compositional formalism <a href="#ref-copestake2005">(Copestake et al., 2005)</a>, and monotonicity-driven inference from Universal Dependency trees <a href="#ref-chen2021">(Chen &amp; Gao, 2021)</a>.</p>
<h2 id="references">
  References
  <a class="heading-link" href="#references">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<ul class="cv-list">
  <li id="ref-montague1973"><strong>Montague, R.</strong> "The proper treatment of quantification in ordinary English." In J. Hintikka, J. Moravcsik, &amp; P. Suppes (Eds.), <em>Approaches to Natural Language</em>, pp. 221–242. Dordrecht: Reidel. 1973.</li>
  <li id="ref-reddy2016"><strong>Reddy, S.</strong>, Oscar Täckström, Michael Collins, Tom Kwiatkowski, Dipanjan Das, Mark Steedman, and Mirella Lapata. "Transforming dependency structures to logical forms for semantic parsing." <em>Transactions of the Association for Computational Linguistics</em>, 4, 127–140. 2016.</li>
  <li id="ref-bai2021"><strong>Bai, J.</strong> and Hai Zhao. "Dep2Sem: Learning the correspondence between dependency trees and formal meaning representations via syntax-guided attention." <em>Findings of the Association for Computational Linguistics: ACL-IJCNLP 2021</em>, pp. 2395–2405. 2021.</li>
  <li id="ref-demarneffe2014"><strong>de Marneffe, M.-C.</strong>, Timothy Dozat, Natalia Silveira, Katri Haverinen, Filip Ginter, Yoav Goldberg, and Christopher D. Manning. "Universal Stanford Dependencies: A cross-linguistic typology." <em>Proceedings of the 9th International Conference on Language Resources and Evaluation (LREC)</em>, pp. 4585–4592. 2014.</li>
  <li id="ref-chen2014"><strong>Chen, D.</strong> and Christopher D. Manning. <a href="https://aclanthology.org/D14-1082/" target="_blank" rel="noopener noreferrer">"A fast and accurate dependency parser using neural networks."</a> <em>Proceedings of the 2014 Conference on Empirical Methods in Natural Language Processing (EMNLP)</em>, pp. 740–750. 2014.</li>
  <li id="ref-brick2007"><strong>Brick, T.</strong> and Matthias Scheutz. "Incremental natural language processing for HRI." <em>Proceedings of the ACM/IEEE International Conference on Human-Robot Interaction</em>, pp. 263–270. 2007.</li>
  <li id="ref-upshotmontague"><strong>Kim, Y.</strong> and Raymond Mooney. <a href="https://github.com/Workday/upshot-montague" target="_blank" rel="noopener noreferrer">upshot-montague</a>: A CCG-based semantic parsing library implementing Montague semantics in Scala. 2013.</li>
  <li id="ref-copestake2005"><strong>Copestake, A.</strong>, Dan Flickinger, Carl Pollard, and Ivan A. Sag. "Minimal recursion semantics: An introduction." <em>Research on Language and Computation</em>, 3(2–3), 281–332. 2005.</li>
  <li id="ref-chen2021"><strong>Chen, Z.</strong> and Qiyue Gao. "Monotonicity marking from Universal Dependency trees." <em>Proceedings of the 14th International Conference on Computational Semantics (IWCS 2021)</em>, pp. 31–41. 2021.</li>
  <li id="ref-dowty1991"><strong>Dowty, D.</strong> "Thematic proto-roles and argument selection." <em>Language</em>, 67(3), 547–619. 1991.</li>
  <li id="ref-levin1993"><strong>Levin, B.</strong> <em>English Verb Classes and Alternations: A Preliminary Investigation</em>. Chicago: University of Chicago Press. 1993.</li>
</ul>
]]></content:encoded><category>nlp</category><category>formal-semantics</category><category>lambda-calculus</category><category>dependency-parsing</category></item><item><title>My Development Environment</title><link>https://alexdjalali.github.io/posts/my-development-environment/</link><guid isPermaLink="true">https://alexdjalali.github.io/posts/my-development-environment/</guid><pubDate>Thu, 15 Jan 2026 00:00:00 +0000</pubDate><dc:creator>a.j.djalali</dc:creator><description>A walkthrough of my macOS development setup: zsh, neovim, tmux, and 80+ CLI tools — all managed from a single dotfiles repo with a one-command install.</description><content:encoded><![CDATA[<p>Every developer I respect has a dotfiles repo. Not because the configs themselves are interesting — most of them aren&rsquo;t — but because maintaining one forces you to make deliberate choices about your tools. You can&rsquo;t version-control a setup you don&rsquo;t understand. The act of curating a dotfiles repo is the act of deciding what matters in your workflow and throwing everything else away.</p>
<p>Mine lives at <a href="https://github.com/alexdjalali/dotfiles"  class="external-link" target="_blank" rel="noopener">github.com/alexdjalali/dotfiles</a>. It provisions a full macOS development environment from a blank machine in a single command. This post walks through what&rsquo;s in it and why.</p>
<h2 id="the-philosophy">
  The Philosophy
  <a class="heading-link" href="#the-philosophy">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Three principles:</p>
<p><strong>One theme everywhere.</strong> <a href="https://catppuccin.com/"  class="external-link" target="_blank" rel="noopener">Catppuccin Mocha</a> in the terminal, in the editor, in tmux, in git diffs, in bat output. Visual consistency across tools isn&rsquo;t vanity — it reduces context-switching friction. When everything looks like it belongs together, your brain stops noticing the seams between tools and starts focusing on the work.</p>
<p><strong>Modern replacements for legacy tools.</strong> <code>ls</code> becomes <code>eza</code>. <code>cat</code> becomes <code>bat</code>. <code>grep</code> becomes <code>ripgrep</code>. <code>find</code> becomes <code>fd</code>. <code>sed</code> becomes <code>sd</code>. <code>du</code> becomes <code>dust</code>. <code>top</code> becomes <code>btop</code>. <code>diff</code> becomes <code>delta</code>. These aren&rsquo;t gimmicks. They&rsquo;re faster, they have better defaults, and they produce output that&rsquo;s actually readable.</p>
<p><strong>One command, full setup.</strong> A fresh Mac should go from &ldquo;nothing installed&rdquo; to &ldquo;fully productive&rdquo; with <code>./install.sh</code>. No manual steps. No &ldquo;oh, I also need to install X.&rdquo; The install script is idempotent — safe to run again whenever I add something new.</p>
<h2 id="the-shell">
  The Shell
  <a class="heading-link" href="#the-shell">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Zsh with <a href="https://ohmyz.sh/"  class="external-link" target="_blank" rel="noopener">Oh My Zsh</a>, <a href="https://github.com/romkatv/powerlevel10k"  class="external-link" target="_blank" rel="noopener">Powerlevel10k</a> for the prompt, and a modular config that keeps <code>.zshrc</code> clean:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span><span style="color:#75715e"># .zshrc is a thin bootstrap that sources conf.d/ modules in order</span>
</span></span><span style="display:flex;"><span>export DOTFILES<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>DOTFILES<span style="color:#66d9ef">:-</span>$HOME/dotfiles<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> conf in <span style="color:#e6db74">&#34;</span>$DOTFILES<span style="color:#e6db74">/zsh/conf.d/&#34;</span>*.zsh<span style="color:#f92672">(</span>N<span style="color:#f92672">)</span>; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>  source <span style="color:#e6db74">&#34;</span>$conf<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span></code></pre></div><p>Each numbered file in <code>conf.d/</code> handles one concern — environment variables, path setup, aliases, completions, fzf config, tool initialization. When I want to change how aliases work, I open <code>04-aliases.zsh</code>. When I want to tweak fzf, I open <code>06-fzf.zsh</code>. No scrolling through a 500-line <code>.zshrc</code> looking for the right section.</p>
<p>The plugins are minimal and deliberate:</p>
<ul>
<li><a href="https://github.com/Aloxaf/fzf-tab"  class="external-link" target="_blank" rel="noopener">fzf-tab</a> — replaces zsh&rsquo;s default completion menu with fzf, so tab completion gets fuzzy matching and preview windows</li>
<li><a href="https://github.com/zsh-users/zsh-autosuggestions"  class="external-link" target="_blank" rel="noopener">zsh-autosuggestions</a> — ghost text from history</li>
<li><a href="https://github.com/zsh-users/zsh-syntax-highlighting"  class="external-link" target="_blank" rel="noopener">zsh-syntax-highlighting</a> — real-time command highlighting</li>
<li><a href="https://github.com/ajeetdsouza/zoxide"  class="external-link" target="_blank" rel="noopener">zoxide</a> — frecency-based <code>cd</code> replacement (learns which directories you visit most)</li>
<li><a href="https://github.com/atuinsh/atuin"  class="external-link" target="_blank" rel="noopener">atuin</a> — shell history with full-text search and optional sync across machines</li>
</ul>
<p>I intentionally removed the default <code>git</code> plugin from Oh My Zsh. It defines 176 aliases, most of which I&rsquo;ll never use, and some of which shadow commands I actually want. My own git aliases live in <code>conf.d/04-aliases.zsh</code> where I control exactly what exists.</p>
<h2 id="tmux">
  tmux
  <a class="heading-link" href="#tmux">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>I spend most of my day inside tmux. The config is hand-rolled with Catppuccin Mocha colors — no plugin themes, just raw color codes mapped to the palette:</p>
<pre tabindex="0"><code class="language-tmux" data-lang="tmux"># Status bar
set -g status-style &#34;bg=#181825,fg=#cdd6f4&#34;
setw -g window-status-current-style &#34;bg=#313244,fg=#cba6f7,bold&#34;

# Panes
set -g pane-border-style &#34;fg=#313244&#34;
set -g pane-active-border-style &#34;fg=#cba6f7&#34;
</code></pre><p>Vim-style keybindings for everything: <code>h/j/k/l</code> for pane navigation, <code>|</code> and <code>-</code> for splits (instead of the unintuitive <code>&quot;</code> and <code>%</code> defaults), <code>v</code> and <code>y</code> in copy mode. True color and undercurl support are configured so Neovim&rsquo;s LSP diagnostic squiggly lines render correctly inside tmux — a detail that took an embarrassing amount of time to get right:</p>
<pre tabindex="0"><code class="language-tmux" data-lang="tmux">set -g default-terminal &#34;tmux-256color&#34;
set -ga terminal-overrides &#34;,*-256color:Tc&#34;
set -as terminal-overrides &#39;,*:Smulx=\E[4::%p1%dm&#39;
set -as terminal-overrides &#39;,*:Setulc=\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&amp;%d::%p1%{255}%&amp;%d%;m&#39;
</code></pre><p><a href="https://github.com/tmux-plugins/tmux-resurrect"  class="external-link" target="_blank" rel="noopener">tmux-resurrect</a> and <a href="https://github.com/tmux-plugins/tmux-continuum"  class="external-link" target="_blank" rel="noopener">tmux-continuum</a> handle session persistence. I can reboot and come back to exactly where I left off — same windows, same panes, same working directories.</p>
<h2 id="neovim">
  Neovim
  <a class="heading-link" href="#neovim">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>AstroNvim-based config, fully Lua, with 30+ plugin configurations. The key pieces:</p>
<ul>
<li><strong>LSP</strong> for every language I work in (Go via <code>gopls</code>, Python via <code>basedpyright</code>, TypeScript via <code>ts_ls</code>, Lua via <code>lua_ls</code>, LaTeX via <code>texlab</code>)</li>
<li><strong>DAP</strong> (Debug Adapter Protocol) for stepping through Go and Python without leaving the editor</li>
<li><strong>Treesitter</strong> for syntax highlighting, text objects, and structural navigation</li>
<li><strong>59 custom LuaSnip snippets for LaTeX</strong> — 20 for TikZ/pgfplots, 17 for Beamer presentations, 22 for common packages and document structures</li>
</ul>
<p>The LaTeX setup deserves a mention. I do academic writing in Neovim with <a href="https://github.com/lervag/vimtex"  class="external-link" target="_blank" rel="noopener">VimTeX</a>, <code>latexmk</code> for continuous compilation, and <a href="https://skim-app.sourceforge.io/"  class="external-link" target="_blank" rel="noopener">Skim</a> for PDF preview with SyncTeX forward/inverse search. Click a line in the PDF, and Neovim jumps to that line in the source. Click a line in Neovim, and Skim jumps to the corresponding position in the PDF. Once you&rsquo;ve experienced this workflow, writing LaTeX in anything else feels broken.</p>
<h2 id="git">
  Git
  <a class="heading-link" href="#git">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The <code>.gitconfig</code> is opinionated:</p>
<pre tabindex="0"><code class="language-gitconfig" data-lang="gitconfig">[core]
	editor = nvim
	pager = delta
	fsmonitor = true
	untrackedCache = true

[commit]
	gpgsign = true

[delta]
	navigate = true
	side-by-side = true
	syntax-theme = &#34;Catppuccin Mocha&#34;

[diff]
	algorithm = histogram

[merge]
	conflictstyle = diff3

[rerere]
	enabled = true

[pull]
	rebase = true

[rebase]
	autoStash = true
	autoSquash = true
</code></pre><p>A few choices worth explaining:</p>
<p><strong>GPG signing on every commit.</strong> Non-negotiable. If your commits aren&rsquo;t signed, anyone can impersonate you with <code>git config user.email</code>.</p>
<p><strong>Delta as the pager.</strong> Side-by-side diffs with syntax highlighting and line numbers, themed to match everything else. <code>git diff</code> output becomes something you actually want to read.</p>
<p><strong>Histogram diff algorithm.</strong> Produces better diffs than the default Myers algorithm, especially for moved blocks of code. Once you switch, you notice the difference immediately and never go back.</p>
<p><strong>rerere enabled.</strong> &ldquo;Reuse recorded resolution&rdquo; — git remembers how you resolved a conflict and applies the same resolution automatically if it encounters the same conflict again. Essential for long-running feature branches that rebase often.</p>
<p><strong>Pull with rebase, rebase with autoStash.</strong> No merge commits from <code>git pull</code>. If I have local changes when pulling, git stashes them automatically, rebases, and pops the stash. Clean linear history with zero effort.</p>
<h2 id="the-brewfile">
  The Brewfile
  <a class="heading-link" href="#the-brewfile">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Everything I install lives in one file. Running <code>brew bundle</code> on a fresh machine installs the entire stack — CLI tools, languages, casks, fonts:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#75715e"># Modern CLI Replacements</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;eza&#34;</span>         <span style="color:#75715e"># ls</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;bat&#34;</span>         <span style="color:#75715e"># cat</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;ripgrep&#34;</span>     <span style="color:#75715e"># grep</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;fd&#34;</span>          <span style="color:#75715e"># find</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;sd&#34;</span>          <span style="color:#75715e"># sed</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;dust&#34;</span>        <span style="color:#75715e"># du</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;btop&#34;</span>        <span style="color:#75715e"># top</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;delta&#34;</span>       <span style="color:#75715e"># diff</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;xh&#34;</span>          <span style="color:#75715e"># curl</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;doggo&#34;</span>       <span style="color:#75715e"># dig</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Languages</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;go&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;python@3&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;uv&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;node&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;pnpm&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;oven-sh/bun/bun&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Kubernetes</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;kubectl&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;k9s&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;helm&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;kind&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;stern&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;lazydocker&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;dive&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;trivy&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Infrastructure</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;terraform&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;pgcli&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;mongosh&#34;</span>
</span></span><span style="display:flex;"><span>brew <span style="color:#e6db74">&#34;grpcurl&#34;</span>
</span></span></code></pre></div><p>The full Brewfile has 80+ entries. The point isn&rsquo;t that I use all of them every day — the point is that when I need <code>k9s</code> or <code>grpcurl</code> or <code>hyperfine</code>, it&rsquo;s already there. The cost of installing something unused is a few hundred megabytes of disk. The cost of needing something that isn&rsquo;t installed is a context switch away from the problem I&rsquo;m solving.</p>
<h2 id="raycast-scripts">
  Raycast Scripts
  <a class="heading-link" href="#raycast-scripts">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p><a href="https://www.raycast.com/"  class="external-link" target="_blank" rel="noopener">Raycast</a> replaced Spotlight on my machine. Beyond its built-in features, I have 29 custom script commands in <code>~/dotfiles/raycast/</code> that automate common workflows:</p>
<ul>
<li><code>focus-mode.sh</code> / <code>end-focus-mode.sh</code> — toggle Do Not Disturb, close distracting apps, set Slack status</li>
<li><code>meeting-mode.sh</code> — same as focus mode but opens Zoom and positions windows</li>
<li><code>open-project.sh</code> — fuzzy-find a project directory and open it in Cursor</li>
<li><code>lazygit-here.sh</code> — open lazygit in the current Finder directory</li>
<li><code>git-status-all.sh</code> — check git status across all project directories at once</li>
<li><code>dev-stack.sh</code> — start/stop the local dev stack (Docker containers, databases)</li>
<li><code>k8s-context.sh</code> — switch kubectl context without remembering cluster names</li>
<li><code>window-layout-coding.sh</code> — arrange windows into my preferred coding layout</li>
</ul>
<p>Each script is a plain bash file with Raycast metadata in the header. No plugins, no dependencies, no framework. When something annoys me more than twice, it becomes a Raycast script.</p>
<h2 id="the-install-script">
  The Install Script
  <a class="heading-link" href="#the-install-script">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The whole thing bootstraps from a blank Mac:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git clone https://github.com/alexdjalali/dotfiles.git ~/dotfiles
</span></span><span style="display:flex;"><span>cd ~/dotfiles
</span></span><span style="display:flex;"><span>./install.sh
</span></span></code></pre></div><p>The script runs 15 steps: Xcode CLT, Homebrew, <code>brew bundle</code>, shell setup, Oh My Zsh with plugins, symlinks (with automatic backup of existing files), fzf integration, bat/delta Catppuccin theme, iTerm2 shell integration, directory scaffolding, TPM and tmux plugins, git-lfs, LaTeX environment, and a headless Neovim bootstrap that syncs all plugins and installs Treesitter parsers.</p>
<p>It&rsquo;s idempotent. Every step checks whether it&rsquo;s already been done before doing it. Symlinks check whether they already point to the right place. Homebrew checks whether it&rsquo;s already installed. The script produces a clean log of what it did and what it skipped:</p>
<pre tabindex="0"><code>[ok]    Homebrew already installed
[ok]    ~/.gitconfig -&gt; ~/dotfiles/git/.gitconfig (already linked)
[info]  Installing Oh My Zsh plugins...
[ok]    fzf-tab already installed
[warn]  Backed up ~/.tmux.conf -&gt; ~/.dotfiles-backup/20260115_143022/
[ok]    ~/.tmux.conf -&gt; ~/dotfiles/tmux/.tmux.conf
</code></pre><h2 id="ai-tool-integration">
  AI Tool Integration
  <a class="heading-link" href="#ai-tool-integration">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The dotfiles also manage my AI coding tool configurations. The same engineering standards get symlinked into three different tools:</p>
<pre tabindex="0"><code>~/.claude/CLAUDE.md        -&gt; ~/dotfiles/.claude/CLAUDE.md
~/.claude/settings.json    -&gt; ~/dotfiles/.claude/settings.json
~/.claude/commands         -&gt; ~/dotfiles/.claude/commands
~/.cursor/rules            -&gt; ~/dotfiles/cursor/rules
~/.kilocode/rules          -&gt; ~/dotfiles/kilocode/rules
</code></pre><p>This means my <a href="https://docs.anthropic.com/en/docs/claude-code"  class="external-link" target="_blank" rel="noopener">Claude Code</a>, <a href="https://www.cursor.com/"  class="external-link" target="_blank" rel="noopener">Cursor</a>, and <a href="https://kilocode.ai/"  class="external-link" target="_blank" rel="noopener">Kilo Code</a> configurations are version-controlled alongside everything else. When I update a standard — say, adding a new quality gate or refining an architecture pattern — the change propagates to every tool the next time I open it. I wrote more about this workflow in <a href="/posts/how-i-learned-to-love-the-bomb/" >How I Learned to Love the Bomb</a>.</p>
<h2 id="the-point">
  The Point
  <a class="heading-link" href="#the-point">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>None of this is about having the fanciest terminal or the most plugins. It&rsquo;s about removing friction. Every tool configured here solves a specific problem I hit repeatedly: navigating code, reviewing diffs, managing containers, switching contexts, staying in flow.</p>
<p>The dotfiles repo is the mechanism that makes this sustainable. Without it, I&rsquo;d spend my first day on any new machine reinstalling things from memory, getting half the configs wrong, and losing a day of productivity. With it, I clone one repo, run one script, and I&rsquo;m working.</p>
<p>The whole thing is MIT-licensed and <a href="https://github.com/alexdjalali/dotfiles"  class="external-link" target="_blank" rel="noopener">on GitHub</a> if any of it is useful to you.</p>
]]></content:encoded><category>developer-tools</category><category>dotfiles</category><category>neovim</category><category>tmux</category><category>macos</category></item></channel></rss>