Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Agent-Friendly Software Architecture

Avatar for Henning Schwentner Henning Schwentner
September 17, 2026
66

Agent-Friendly Software Architecture

Avatar for Henning Schwentner

Henning Schwentner

September 17, 2026

Transcript

  1. Give the Agent Context what the agent sees Market Square

    Mill Way Quayside City Park hschwentner.io
  2. Good context is not “as much context as possible,” but

    “the right slice of context” hschwentner.io
  3. Structured Programming callable unit callable Edgar Dijkstra: Go To Statement

    Considered Harmful callable unit unit Edsger Dijkstra hschwentner.io 1968
  4. Architecture Term: Module accessible A part of a software that

    hides its implementation/a decision behind an interface. interface Study it without reading the rest. implementation e.g. a method, a class, a bounded context hschwentner.io hidden
  5. Separation of Concerns “…study in depth an aspect in isolation

    […], all the time knowing that one is occupying oneself only with one of the aspects.” Edsger Dijkstra one aspect in isolation 1974 hschwentner.io
  6. Loose Coupling/ High Cohesion 1979 knowledge needed to work on

    the left Ed Larry module hschwentner.io Yourdon Constantine
  7. Cognitive Load 1956 everything else in the repo 40 files

    of context the actual task 1988 hschwentner.io George A. Miller John Sweller
  8. Hypothesis 1: To keep the “cognitive load” of an agent

    right, keep the context window right
  9. Hypothesis 2: the architecture that makes a codebase legible to

    an agent is the same architecture that has always made it legible to humans
  10. ARCHITECTURERULES.md # Architecture Rules How this system is built. Not

    a survey of what exists. Prescription, not description → The current architecture lives in ARCHITECTURE.md ## Style - Hexagonal, not layered. Adapters depend inward; the domain depends on nothing. [DependencyRulesTest] - Domain imports no framework types. [DomainPurityTest] - One module per bounded context; modules talk through published interfaces, never through each other's tables. [ModuleApiTest] Style, not domain. ## Choices, where others are also reasonable - Ports live beside the domain, not in their own package. [PortLocationTest] - Mapping is hand-written. No MapStruct. [no test] - Persistence returns domain types, not entities. [no test] The contexts and their language live in CONTEXT-MAP.md. ## Enforcement Every rule here is an ArchUnit test. Rules marked [no test] are not yet enforced: if you touch code they govern, write the test first. Make it fail, then fix the code. Where a test and this file disagree, the file wins — fix the test. Where code breaks a rule and the suite stays green, the test is missing. All can be referenced from AGENTS.md. ## Known deviations - sales reads risk tables directly (owner: HS, #412). Do not add to this. New code goes through the port. hschwentner.io
  11. ARCHITECTURERULES.md # Architecture Rules Style, not domain. The contexts and

    their How this system should be built. language live in CONTEXT## Style MAP.md. - Hexagonal, not layered. Adapters depend inward; the # Architecture Rules How this system is built. Not a survey of what exists. Not a survey of what exists. domain depends on nothing. [DependencyRulesTest] - Domain imports no framework types. [DomainPurityTest] - One module per bounded context; modules talk through published interfaces, never through each other's tables. [ModuleApiTest] Both referenced from AGENTS.md. ## Choices, where others are also reasonable - Ports live beside the domain, not in their own package. [PortLocationTest] - Mapping is hand-written. No MapStruct. [no test] - Persistence returns domain types, not entities. [no test] ## Enforcement Every rule here is an ArchUnit test. Rules marked [no test] are not yet enforced: if you touch code they govern, write the test first. Make it fail, then fix the code. Where a test and this file disagree, the file wins — fix the test. Where code breaks a rule and the suite stays green, the test is missing. ## Known deviations - sales reads risk tables directly (owner: HS, #412). Do not add to this. New code goes through the port. hschwentner.io
  12. ARCHITECTURERULES.md ## Style # Architecture Rules How this system is

    built. Not a survey of what exists. ## Style - Hexagonal, not layered. Adapters depend inward; the domain depends on nothing. [DependencyRulesTest] - Domain imports no framework types. [DomainPurityTest] - One module per bounded context; modules talk through published interfaces, never through each other's tables. [ModuleApiTest] Style, not domain. The contexts and their language live in CONTEXTdepend inward; the domain depends MAP.md. - Hexagonal, not layered. Adapters on nothing. ## Choices, where others are also reasonable - Ports live beside the domain, not in their own package. [PortLocationTest] - Mapping is hand-written. No MapStruct. [no test] - Persistence returns domain types, not entities. [no test] [DependencyRulesTest] Both referenced from AGENTS.md. - Domain imports no framework types. [DomainPurityTest] - One module per bounded context; ## Enforcement Every rule here is an ArchUnit test. Rules marked [no test] are not yet enforced: if you touch code they govern, write the test first. Make it fail, then fix the code. Where a test and this file disagree, the file wins — fix the test. Where code breaks a rule and the suite stays green, the test is missing. modules talk through published interfaces. [ModuleApiTest] ## Known deviations - sales reads risk tables directly (owner: HS, #412). Do not add to this. New code goes through the port. hschwentner.io
  13. ARCHITECTURERULES.md # Architecture Rules How this system is built. Not

    a survey of what exists. ## Choices, where others are also reasonable ## Style - Hexagonal, not layered. Adapters depend inward; the domain depends on nothing. [DependencyRulesTest] - Domain imports no framework types. [DomainPurityTest] - One module per bounded context; modules talk through published interfaces, never through each other's tables. [ModuleApiTest] Style, not domain. The contexts and their language live in CONTEXT- Ports live beside the domain.MAP.md. ## Choices, where others are also reasonable - Ports live beside the domain, not in their own package. [PortLocationTest] - Mapping is hand-written. No MapStruct. [no test] - Persistence returns domain types, not entities. [no test] [PortLocationTest] Both referenced from AGENTS.md. - Mapping is hand-written. No MapStruct. [no test] ## Enforcement Every rule here is an ArchUnit test. Rules marked [no test] are not yet enforced: if you touch code they govern, write the test first. Make it fail, then fix the code. Where a test and this file disagree, the file wins — fix the test. Where code breaks a rule and the suite stays green, the test is missing. - Persistence returns domain types. [no test] ## Known deviations What (owner: the model cannot - sales reads risk tables directly HS, #412). Do not add to this. New code goes through the port. guess. hschwentner.io
  14. ARCHITECTURERULES.md # Architecture Rules How this system is built. Not

    a survey of what exists. ## Style - Hexagonal, not layered. Adapters depend inward; the domain depends on nothing. [DependencyRulesTest] - Domain imports no framework types. [DomainPurityTest] - One module per bounded context; modules talk through published interfaces, never through each other's tables. [ModuleApiTest] ## Enforcement Style, not domain. The contexts and their language live in CONTEXT- Ports live beside the domain, not in their own Rules marked [no test] are not yet MAP.md. package. [PortLocationTest] Every rule here is an ArchUnit test. ## Choices, where others are also reasonable - Mapping is hand-written. No MapStruct. [no test] - Persistence returns domain types, not entities. [no test] enforced: if you touch code theyBoth referenced from govern, write the test first. ## Enforcement Every rule here is an ArchUnit test. Rules marked [no test] are not yet enforced: if you touch code they govern, write the test first. Make it fail, then fix the code. Where a test and this file disagree, the file wins — fix the test. Where code breaks a rule and the suite stays green, the test is missing. AGENTS.md. Make it fail, then fix the code. Where a test and this file disagree, ## Known deviations - sales reads risk tables directly (owner: HS, #412). Do not add to this. New code goes through the port. the file wins - fix the test. Suite green on broken rule? Test missing. hschwentner.io
  15. ARCHITECTURERULES.md # Architecture Rules How this system is built. Not

    a survey of what exists. ## Style - Hexagonal, not layered. Adapters depend inward; the domain depends on nothing. [DependencyRulesTest] - Domain imports no framework types. [DomainPurityTest] - One module per bounded context; modules talk through published interfaces, never through each other's tables. [ModuleApiTest] ## Known deviations ## Choices, where others are also reasonable - Ports live beside the domain, not in their own package. [PortLocationTest] - Mapping is hand-written. No MapStruct. [no test] - Persistence returns domain types, not entities. [no test] - sales reads risk tables directly. owner: HS, issue #412 ## Enforcement Every rule here is an ArchUnit test. Rules marked [no test] are not yet enforced: if you touch code they govern, write the test first. Make it fail, then fix the code. Where a test and this file disagree, the file wins — fix the test. Where code breaks a rule and the suite stays green, the test is missing. Do not add to this. New code goes through ## Known deviations - sales reads risk tables directly (owner: HS, #412). Do not add to this. New code goes through the port. the port. Honest about what is already broken. hschwentner.io
  16. Bibliography Cockburn, Alistair. “Hexagonal Architecture.” January 4, 2005. https://alistair.cockburn.us/hexagonal-architecture/. Dijkstra,

    Edsger. “Go To Statement Considered Harmful.” Communications of the ACM 11, no. 3 (March 1968): 147–48. Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. Boston: Addison-Wesley, 2004. Lilienthal, Carola and Henning Schwentner. Domain-Driven Transformation: Modernize Legacy Software and Mitigate Risk. Santa Rosa, CA: O’Reilly, 2026. Parnas, David L. “On the Criteria To Be Used in Decomposing Systems into Modules.” Communications of the ACM 15, no. 12 (December 1972): 1053–58. Yourdon, Edward and Larry L. Constantine. Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design. New York: Yourdon Press, 1979. hschwentner.io
  17. AI Term: Large Language Model a.k.a. LLM, foundation model, frontier

    model, model Predicts the next →token from those before it. No memory between runs. It knows nothing about your repository except what is in the →context window. e.g. Sonnet, GPT, Gemini—the model matters less than the codebase. hschwentner.io
  18. AI Term: Prompt a.k.a. instruction, request What you ask for,

    in words, plus whatever the →harness attaches to it. It is the smallest part of the →context window. The files around it do most of the work. e.g. “Add VAT to the invoice total”—four words, and forty files of context. hschwentner.io
  19. AI Term: Token a.k.a. subword The unit a →model reads

    and writes—not a word, not a line. Roughly four characters of English. Limits and bills are counted in tokens. e.g. getCustomerById is about four tokens: get · Customer · By · Id. hschwentner.io
  20. AI Term: Context Window a.k.a. prompt budget Everything the model

    sees at once: →prompt, files, its own output. Measured in →tokens. Outside the context window nothing exists—it cannot look anything up. e.g. 200,000 tokens is roughly 15,000 lines of code, answer included. hschwentner.io
  21. AI Term: Lost in the Middle a.k.a. position bias A

    model attends most to the beginning and the end of its context. What sits in the middle is weighted least. So, a full →context window is not a well-read one. e.g. the rule it needed sat in file 30 of 60; it used the one from file 2. hschwentner.io
  22. AI Term: Coding Agent a.k.a. agent, coding assistant An →LLM

    in a loop with tools. It reads, greps, edits, runs the tests—and picks what to read next. An assistant completes your line. An agent chooses its own context. e.g. “Add VAT to the invoice total”: it greps, opens three files, edits one. hschwentner.io
  23. AI Term: Harness a.k.a. agent scaffold, agent loop The program

    around the →model: it picks what enters the →context window. The model is fixed; the harness is not. Your repository is half of it. e.g. Claude Code and Cursor run the same models, and behave differently. hschwentner.io
  24. AI Term: Context Rot a.k.a. context degradation, context pollution Answers

    get worse as the context window fills up. Stale plans, dead ends, and its own old output crowd the window. A long session degrades even inside the limit. e.g. hour three: it re-implements what it deleted in hour one. hschwentner.io
  25. AI Term: Hallucination a.k.a. confabulation Output that is fluent, plausible,

    and not true. The model fills a gap with what usually follows. Missing context is the gap. e.g. a method name that should exist by every convention here, but doesn’t. hschwentner.io
  26. Codebase A Agent nails it. Clean diff. First try. Codebase

    B Agent flails. Hallucinated methods. Broken tests.
  27. It weights what it read first and last. Everything in

    between gets diluted. A small, self-contained slice beats a large, complete one.
  28. The context window has a size. And it fills up

    fast. Every extra file costs more and buys noise.
  29. Structure alone gets an LLM surprisingly far. Names help most

    where the domain isn’t a textbook one.
  30. Big Ball of Mud Clean bounded context Noisy suggestions. Coherent

    suggestions. Brittle changes. Local changes. Risky refactors. Safe refactors.
  31. A hallucinated method name that “should” exist—but doesn’t. A “fix”

    that duplicates logic instead of finding the one true place for it. A one-line domain change that becomes a 12-file agent diff.
  32. Same task, same agent, same prompt: it can’t tell where

    risk logic ends and sales logic begins.