{"id":23609,"date":"2026-08-31T21:07:00","date_gmt":"2026-08-31T21:07:00","guid":{"rendered":"https:\/\/scannn.com\/domain-driven-agents\/"},"modified":"2026-08-31T21:07:00","modified_gmt":"2026-08-31T21:07:00","slug":"domain-driven-agents","status":"publish","type":"post","link":"https:\/\/scannn.com\/lv\/domain-driven-agents\/","title":{"rendered":"Domain-Driven Agents"},"content":{"rendered":"\n<div>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">I&#8217;ve been using LLMs heavily in the last years in coding, or more generally, in software<br \/>\nengineering. I watched many times what productivity boost I could gain from it, and I used LLMs in more and more of my projects. It works well in greenfield projects,<br \/>\nand small ones. The reality is that in day to day work we need to introduce agents into legacy codebases with heavy dependency trees, strong coupling, and a tech debt backlog full of everything we never got to. We quickly notice that the quality of work LLMs can deliver drops sharply.<\/p>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">The failure has a specific shape. Ask for a &#8220;job offer status&#8221; field in a<br \/>\ngreenfield repo and you get one. Ask for it in a system that has been shipping for four years<br \/>\nand the model invents a fourth spelling of a concept that already exists three times, because<br \/>\nthe codebase itself never decided which one was real. It writes an adapter where a call was<br \/>\nfine, or calls straight through where an adapter was the whole point. Every one of those is a<br \/>\nquestion about the system that the system does not answer anywhere. The model guesses, and often guesses wrong.<\/p>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">So brownfield projects are deep, and technical depth is only the first layer. Underneath sits a<br \/>\nsecond one: confusion, missing meaning, and no shared language to resolve it in. That is<br \/>\nthe layer the model falls into. The model is not what needs upgrading. <strong>The code is not<br \/>\nready<\/strong>, and readiness is something we can build. Incrementally. Piece by piece.<br \/>\nLet me show you how I do it.<\/p>\n<h2 class=\"group font-bold text-foreground text-2xl\" id=\"it-is-easier-than-before\">It is easier than before<a href=\"#it-is-easier-than-before\" aria-label=\"Link to this section\" class=\"mx-1 -my-4 inline-flex min-h-11 min-w-11 items-center justify-center align-middle text-primary no-underline opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 [@media(pointer:coarse)]:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\">#<\/a><\/h2>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">At the beginning of software engineering there was the one and only: tech debt. It&#8217;s a natural<br \/>\nconsequence of what we, as devs, are trying to achieve. We&#8217;re not ready for business decisions<br \/>\nfrom the future shifting our current view of the code. We need to deliver, and deliver fast,<br \/>\npaying some tradeoffs. As a consequence, code smell grows bigger and bigger. The usual answer<br \/>\nis to spend part of the engineering budget on cleanups: earmark 10-20% of the technology budget for<br \/>\nresolving tech debt. In theory&#8230; In the next quarter&#8230;<\/p>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">A fifth of the budget is the toll on <em>deciding<\/em> what should change and then <em>typing<\/em> it out, and those two halves have never had the same price. Deciding stayed about as expensive as it was. Typing it out collapsed. An LLM will do the mechanical half of a cleanup (the extracted module, a refactor across two packages, more test coverage)<br \/>\nat a cost that no longer resembles 2020. Paying tech debt still takes time. It takes<br \/>\nsignificantly less of it, and what is left for me is the deciding part.<\/p>\n<h3 class=\"group font-bold text-foreground text-xl\" id=\"strategic-vs-tactical\">Strategic vs tactical<a href=\"#strategic-vs-tactical\" aria-label=\"Link to this section\" class=\"mx-1 -my-4 inline-flex min-h-11 min-w-11 items-center justify-center align-middle text-primary no-underline opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 [@media(pointer:coarse)]:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\">#<\/a><\/h3>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">I split the work in two, and I&#8217;ll borrow the words from John Ousterhout&#8217;s <em>A Philosophy of<br \/>\nSoftware Design<\/em> while being honest that I&#8217;m bending them. He uses <strong>tactical<\/strong> and<br \/>\n<strong>strategic<\/strong> for two attitudes you can hold while coding: tactical programming is<br \/>\ngetting-it-working-now, strategic programming is investing in the design as you go. I use the<br \/>\nsame pair for a split of <em>authorship<\/em>, because the economics above cut along that line.<br \/>\n<strong>Strategic<\/strong> work is deciding: reading the system, working out what has to change and why, and<br \/>\nwhether the change actually serves the feature. <strong>Tactical<\/strong> work is carrying that decision<br \/>\ninto the files. The first is the part that needs the system in your head. The second is the<br \/>\npart that got cheap.<\/p>\n<h4 class=\"group font-bold text-foreground text-lg\" id=\"what-i-do\">What I do<a href=\"#what-i-do\" aria-label=\"Link to this section\" class=\"mx-1 -my-4 inline-flex min-h-11 min-w-11 items-center justify-center align-middle text-primary no-underline opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 [@media(pointer:coarse)]:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\">#<\/a><\/h4>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">In the first one I&#8217;m fully involved and<br \/>\nin the second one I&#8217;m rather a reviewer than an implementer. In the first path I analyze the<br \/>\ncodebase in a more generic way, assessing the changes that need to be implemented and their<br \/>\nalignment to the features I want to deliver. The effect of those approaches is GitHub issues I<br \/>\ncreate in each repository.<\/p>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">The issues are then addressed by my AI system based on <strong>skills<\/strong> and <strong>sub-agents<\/strong>. A skill<br \/>\nis a written procedure: a markdown file of instructions the model loads when the task matches<br \/>\nit, so &#8220;address an issue&#8221; or &#8220;regenerate the context map&#8221; runs the same way every time instead<br \/>\nof the way I happened to phrase it that morning. A sub-agent is a separate model session with<br \/>\nits own fresh context and its own narrow job (implement, review for security, review against<br \/>\nthe spec), reporting back a result rather than dumping its whole transcript into mine.<\/p>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">When they are implemented, PRs are ready to jump into. I go through the review sessions,<br \/>\naccepting the changes or asking for some improvements. I can do that incrementally, caring<br \/>\nabout the test coverage and about who breaks: before a change lands I need to know which other<br \/>\nparts of the system consume the thing I&#8217;m touching, and whether the change is one they can<br \/>\nsurvive. Now, as a software engineer, I coordinate, I plan, and I create a path for the<br \/>\nimprovements. But at that point I don&#8217;t need to implement that by myself. The time is saved.<\/p>\n<figure role=\"img\" aria-label=\"The split of authorship between strategic and tactical work: assessing the codebase produces GitHub issues, the issues are addressed by skills and sub-agents, and their work arrives as a pull request. My review is the only gate: it either sends the task back to the agents for improvements or accepts it into the merged codebase.\" class=\"mx-auto max-w-full rounded-lg\"><\/figure>\n<h2 class=\"group font-bold text-foreground text-2xl\" id=\"ddd-as-a-fundament\">DDD as a fundament<a href=\"#ddd-as-a-fundament\" aria-label=\"Link to this section\" class=\"mx-1 -my-4 inline-flex min-h-11 min-w-11 items-center justify-center align-middle text-primary no-underline opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 [@media(pointer:coarse)]:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\">#<\/a><\/h2>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">That leaves the strategic half, and it is worth exactly as much as the language it is written<br \/>\nin. This is where DDD comes in.<\/p>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">DDD was always one of my choices for software I could still change a year later. The<br \/>\napproach presented by Eric Evans gave us a way to shrink the communication gap between the<br \/>\nbusiness and the technical side. Domain-driven design, based on ubiquitous language and bounded<br \/>\ncontexts, translates what the business needs directly into the technical part. Both sides talk<br \/>\nin the same language. With agents in the loop, that link matters even more: it is how we state<br \/>\nour needs to the model and how we read its reasoning back. That is why I build on it so heavily.<\/p>\n<h4 class=\"group font-bold text-foreground text-lg\" id=\"what-i-do-1\">What I do<a href=\"#what-i-do-1\" aria-label=\"Link to this section\" class=\"mx-1 -my-4 inline-flex min-h-11 min-w-11 items-center justify-center align-middle text-primary no-underline opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 [@media(pointer:coarse)]:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\">#<\/a><\/h4>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">Every repository I own carries a <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">.workflow.json<\/code> at its root. It is my own manifest, the place a repo tells my tooling what it is: which languages it holds, which directories an agent should read first, which checks have to pass before work in it can ship. One block in it is about the domain, and declaring that block is<br \/>\nthe only registration a repo needs. There is no second registry to drift out of sync.<\/p>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">The block names the project, its bounded contexts, where each context&#8217;s glossary lives, its<br \/>\nsubdomain type, and every edge to a neighbouring context. The example comes from a project of mine, <strong>job-offer-box<\/strong>, a job application tracker built as two<br \/>\nrepositories, a Rust backend I keep under the <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">hyperion<\/code> project and a web frontend. Here is<br \/>\nthe frontend&#8217;s manifest, trimmed to a single edge:<\/p>\n<figure data-rehype-pretty-code-figure=\"\">\n<pre class=\"max-w-full overflow-x-auto rounded-lg border border-border p-4\" style=\"background-color:var(--shiki-bg);color:var(--shiki-fg)\" tabindex=\"0\" data-language=\"json\" data-theme=\"brand-css-vars\"><code data-language=\"json\" data-theme=\"brand-css-vars\" style=\"display:grid\"><span data-line=\"\"><span style=\"color:var(--shiki-fg)\">{<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">  \"domain\"<\/span><span style=\"color:var(--shiki-fg)\">: {<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">    \"project\"<\/span><span style=\"color:var(--shiki-fg)\">: <\/span><span style=\"color:var(--shiki-token-string)\">\"job-offer-box\"<\/span><span style=\"color:var(--shiki-fg)\">,<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">    \"contexts\"<\/span><span style=\"color:var(--shiki-fg)\">: [<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-fg)\">      {<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">        \"name\"<\/span><span style=\"color:var(--shiki-fg)\">: <\/span><span style=\"color:var(--shiki-token-string)\">\"job-box-web\"<\/span><span style=\"color:var(--shiki-fg)\">,<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">        \"docs\"<\/span><span style=\"color:var(--shiki-fg)\">: <\/span><span style=\"color:var(--shiki-token-string)\">\"CONTEXT.md\"<\/span><span style=\"color:var(--shiki-fg)\">,<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">        \"subdomain\"<\/span><span style=\"color:var(--shiki-fg)\">: <\/span><span style=\"color:var(--shiki-token-string)\">\"supporting\"<\/span><span style=\"color:var(--shiki-fg)\">,<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">        \"edges\"<\/span><span style=\"color:var(--shiki-fg)\">: [<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-fg)\">          {<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">            \"to\"<\/span><span style=\"color:var(--shiki-fg)\">: <\/span><span style=\"color:var(--shiki-token-string)\">\"hyperion\/job-offer-backend\"<\/span><span style=\"color:var(--shiki-fg)\">,<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">            \"direction\"<\/span><span style=\"color:var(--shiki-fg)\">: <\/span><span style=\"color:var(--shiki-token-string)\">\"outbound\"<\/span><span style=\"color:var(--shiki-fg)\">,<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">            \"pattern\"<\/span><span style=\"color:var(--shiki-fg)\">: <\/span><span style=\"color:var(--shiki-token-string)\">\"unclassified\"<\/span><span style=\"color:var(--shiki-fg)\">,<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">            \"owner\"<\/span><span style=\"color:var(--shiki-fg)\">: <\/span><span style=\"color:var(--shiki-token-string)\">\"supplier\"<\/span><span style=\"color:var(--shiki-fg)\">,<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">            \"shape\"<\/span><span style=\"color:var(--shiki-fg)\">: <\/span><span style=\"color:var(--shiki-token-string)\">\"codegen from the backend's document (scripts\/generate-api.ts:12) ... conformist on write (src\/lib\/api\/jobs.ts:37), ACL on read (src\/lib\/api\/adapters\/offer.ts:50)\"<\/span><span style=\"color:var(--shiki-fg)\">,<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-token-string)\">            \"note\"<\/span><span style=\"color:var(--shiki-fg)\">: <\/span><span style=\"color:var(--shiki-token-string)\">\"conformist on write and an anticorruption layer on read; two patterns hold at once, so neither name alone is true\"<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-fg)\">          }<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-fg)\">        ]<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-fg)\">      }<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-fg)\">    ]<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-fg)\">  }<\/span><\/span>\n<span data-line=\"\"><span style=\"color:var(--shiki-fg)\">}<\/span><\/span><\/code><\/pre>\n<\/figure>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">Read it in order. <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">to<\/code> is the address: which context on the other end. <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">direction<\/code> says who&#8217;s<br \/>\ncalling whom; the web repo calls the backend, so <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">outbound<\/code> (the backend&#8217;s own manifest declares<br \/>\nthe same edge <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">inbound<\/code>). <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">owner<\/code> says whose model wins if the two sides ever disagree: the<br \/>\nbackend&#8217;s, so <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">supplier<\/code>. <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">pattern<\/code> is the relationship itself, picked from a closed vocabulary;<br \/>\nhere it&#8217;s <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">unclassified<\/code>, because the web repo does two different things at once. It accepts the<br \/>\nbackend&#8217;s shape as-is when writing and translates it into its own shape when reading. The<br \/>\n<code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">note<\/code> spells that out; a single label would be right about one case and wrong about the other.<\/p>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">Beside the manifest sits a <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">CONTEXT.md<\/code> per context, the living glossary with the precise meaning<br \/>\nof every term and the deliberately rejected synonyms. Two files per context, both owned by the repo that owns the code. Nothing<br \/>\nabove them is authored: the context map (the one document showing every context in the portfolio<br \/>\nand every edge between them) is derived. A generator, a script that walks every repo<br \/>\non disk, unions the <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">domain<\/code> blocks and emits it as a single <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">CONTEXT-MAP.md<\/code>. The map is<br \/>\ndisposable and regenerable.<\/p>\n<figure role=\"img\" aria-label=\"The two files each repository authors and the one map derived from them: each repo carries a .workflow.json domain block and a CONTEXT.md glossary for its context, the generator reads only the manifests and emits the portfolio context map plus the findings it files as issues, while the web context's glossary conforms to the backend's published language.\" class=\"mx-auto max-w-full rounded-lg\"><img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/coldtake.dev\/diagrams\/ubiquitous-language-map.svg\" alt=\"\" aria-hidden=\"true\" width=\"470\" height=\"478\" class=\"mx-auto block h-auto max-w-full\"\/><\/figure>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">Back to <strong>job-offer-box<\/strong>. <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">hyperion\/job-offer-backend<\/code> owns the product language. It persists<br \/>\n<strong>Job Offer<\/strong>, <strong>Profile<\/strong>, <strong>Profile Variant<\/strong>, <strong>Resume<\/strong>, <strong>Cover Letter<\/strong>, under the rule<br \/>\nthat where two contexts author the same term, the one holding the durable state owns it.<br \/>\n<code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">job-offer-box\/job-box-web<\/code> owns only the screen vocabulary (<strong>View Model<\/strong>, <strong>Filter State<\/strong>,<br \/>\n<strong>Facet Stats<\/strong>) and marks everything else <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">[published]<\/code>, arriving verbatim as generated<br \/>\nTypeScript from the backend&#8217;s OpenAPI document. That is the level of precision an agent needs.<br \/>\nPoint it at the web repo and it knows that renaming <strong>Job Offer<\/strong> there belongs to the backend,<br \/>\nthat the adapters on the read path exist on purpose, and which words it is allowed to invent.<br \/>\nWith the map the model knows which context it is in, and with the glossary it knows the words used there.<\/p>\n<h4 class=\"group font-bold text-foreground text-lg\" id=\"both-sides-declare-so-disagreement-is-mechanical\">Both sides declare, so disagreement is mechanical<a href=\"#both-sides-declare-so-disagreement-is-mechanical\" aria-label=\"Link to this section\" class=\"mx-1 -my-4 inline-flex min-h-11 min-w-11 items-center justify-center align-middle text-primary no-underline opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 [@media(pointer:coarse)]:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\">#<\/a><\/h4>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">Every edge is declared twice, once from each side, and that duplication is the whole point. The<br \/>\ngenerator cross-checks the pairs, and it is careful about what counts as a disagreement: a<br \/>\nsupplier names its own stance (<code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">published-language<\/code>), a consumer names its own (<code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">conformist<\/code>,<br \/>\n<code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">anticorruption-layer<\/code>), so the check is a pairing table.<\/p>\n<figure role=\"img\" aria-label=\"What the context-map skill checks: only the supplier and consumer declarations of an edge, paired one way. The check reports four kinds of finding: an address nobody declares, patterns that do not pair, directions that do not mirror, and an edge only one side declares. A valid pair is a supplier pattern matched with a compatible consumer pattern.\" class=\"mx-auto max-w-full rounded-lg\"><img decoding=\"async\" src=\"https:\/\/coldtake.dev\/diagrams\/context-map-checks.svg\" alt=\"\" aria-hidden=\"true\" width=\"454\" height=\"460\" class=\"mx-auto block h-auto max-w-full\"\/><\/figure>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">I run it as a skill, at three moments: when I have touched a manifest, when I am onboarding a<br \/>\nrepo, and before I change anything another context depends on. Each disagreement it reports is a<br \/>\n<strong>finding<\/strong>: one edge, one way the two declarations fail to fit. With one flag, the skill files<br \/>\neach one as a <code data-mdx-inline-code=\"\" class=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-primary\">DDD<\/code> issue on the repo that owns the wrong side. The issue carries a fingerprint<br \/>\n(the kind of finding plus the two addresses), so a re-run after a half-fix updates the same issue<br \/>\ninstead of opening a second one, and a finding that no longer appears closes its issue. From<br \/>\nthere it follows the same spine as everything else here: an issue, an agent, a PR, my review.<\/p>\n<figure role=\"img\" aria-label=\"How a disagreement about shared language becomes work: changing an edge re-runs the context-map skill, which reports findings (an unknown address, an unpaired pattern, or an edge only one side declared) and files each as a DDD issue on the repo that stayed silent. Settling it means changing both manifests; only a re-run with no findings leaves the map agreeing with itself.\" class=\"mx-auto max-w-full rounded-lg\"><img decoding=\"async\" src=\"https:\/\/coldtake.dev\/diagrams\/context-map-loop.svg\" alt=\"\" aria-hidden=\"true\" width=\"500\" height=\"608\" class=\"mx-auto block h-auto max-w-full\"\/><\/figure>\n<h2 class=\"group font-bold text-foreground text-2xl\" id=\"what-comes-next\">What comes next<a href=\"#what-comes-next\" aria-label=\"Link to this section\" class=\"mx-1 -my-4 inline-flex min-h-11 min-w-11 items-center justify-center align-middle text-primary no-underline opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 [@media(pointer:coarse)]:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\">#<\/a><\/h2>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">That is the strategic layer, and it is already in place. It settles where a context ends<br \/>\nand how it talks to its neighbours: the shape of the map. The inside of any single context is<br \/>\nstill ordinary code that lets you build a nonsense object and save it.<\/p>\n<p class=\"text-lg leading-[1.7] text-muted-foreground\">With the context map in place and the glossary defined, I can focus on the codebase itself: taking one context at a time and migrating it to a real domain model built from DDD primitives (value objects, aggregates, domain services and others). That process makes the codebase<br \/>\nanswer the questions the model was guessing at: what this word means, who owns it, where this<br \/>\ncontext stops. I&#8217;ll share the whole system shortly, with the skills ready to use. Subscribe so you don&#8217;t miss it.<\/p>\n<\/div>\n<p><a href=\"https:\/\/coldtake.dev\/blog\/domain-driven-agents?utm_source=tldrdev\">Source link <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve been using LLMs heavily in the last years in coding, or more generally, in software engineering. I watched many times what productivity boost I could gain from it, and I used LLMs in more and more of my projects. It works well in greenfield projects, and small ones. The reality is that in day [&hellip;]<\/p>\n","protected":false},"author":16,"featured_media":23610,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[143],"tags":[],"class_list":["post-23609","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai"],"_links":{"self":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts\/23609","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/users\/16"}],"replies":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/comments?post=23609"}],"version-history":[{"count":0,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts\/23609\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/media\/23610"}],"wp:attachment":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/media?parent=23609"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/categories?post=23609"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/tags?post=23609"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}