<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Software Architecture on James Sawle</title><link>https://jamessawle.dev/categories/software-architecture/</link><description>Recent content in Software Architecture on James Sawle</description><generator>Hugo</generator><language>en-GB</language><atom:link href="https://jamessawle.dev/categories/software-architecture/index.xml" rel="self" type="application/rss+xml"/><item><title>Architecture tests read structure. Architecture lives in intent</title><link>https://jamessawle.dev/writing/architecture-tests-read-structure-architecture-lives-in-intent/</link><guid isPermaLink="true">https://jamessawle.dev/writing/architecture-tests-read-structure-architecture-lives-in-intent/</guid><pubDate>Thu, 17 Sep 2026 00:00:00 +0000</pubDate><description>&lt;p&gt;Two endpoints on one controller. They call the same service method, reach the
same repository, and finish at the same write to the same table. Both contain a
validation call. One does what the team decided its architecture should do, and
the other does not.&lt;/p&gt;
&lt;p&gt;Run every architecture test you have against that pair and they come back
identical, because structurally they &lt;em&gt;are&lt;/em&gt; identical: same components, same
package boundaries, same dependency directions, same operations against the same
infrastructure. The difference is a legacy branch behind a flag nobody removed,
where the write happens without the request ever passing through validation.&lt;/p&gt;
&lt;p&gt;The decision was &lt;em&gt;requests are validated before they change state&lt;/em&gt;. What the
team can assert is that a class in one package does not reference a class in
another. Passing that assertion is not evidence for that decision.&lt;/p&gt;
&lt;p&gt;I have argued before that an agent&amp;rsquo;s confidence has to come from &lt;a href="https://jamessawle.dev/writing/agents-write-code-faster-xp-makes-change-safer/"&gt;evidence
outside it&lt;/a&gt; — tests,
compilation, static analysis, contracts — and then that &lt;a href="https://jamessawle.dev/writing/the-agent-writes-the-code-the-team-still-pairs/"&gt;those checks do not
automatically
express&lt;/a&gt; everything a
team values about architecture, operability or maintainability. This picks up
that unfinished second thread at the architecture layer, where it matters most
when an agent is implementing. The agent sees green tests, a green dependency
rule and a plausible review, and concludes the work is done. Nothing in that
loop could notice the branch. The checks described the implementation
accurately. They just had no way to describe the architecture.&lt;/p&gt;
&lt;p&gt;&lt;img class="article-figure" src="bypassed-boundary.svg" alt="Two endpoints on one controller call the same service and finish at the same database write. On the first, every request crosses a validation boundary before the write. On the second, a legacy branch behind an old flag reaches the same write without crossing it." loading="lazy" decoding="async" title="Identical components, dependencies and effects; only the execution paths differ."&gt;
&lt;/p&gt;
&lt;h2 id="the-tools-are-not-the-problem"&gt;The tools are not the problem&lt;/h2&gt;
&lt;p&gt;Architecture tests understand far more than dependency direction, and an
argument built on the opposite claim is a strawman. ArchUnit exposes packages,
classes, dependencies, inheritance, annotations, field access and method calls,
and a custom rule is ordinary Java, so you can assert anything you can
compute.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; jQAssistant scans a project into a graph and evaluates
Cypher constraints; its own introductory example finds test methods containing
no assertion, not a package edge.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; Semgrep&amp;rsquo;s taint mode expresses
sources, propagators, sanitizers and sinks.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;CodeQL will do whole-program data flow, and is candid about the price: global
flow costs materially more time and memory, is less precise than local flow,
and wants a purpose-built model of sources, sinks, barriers and framework edges
first.&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; It also, to concede the point up front, goes furthest towards
what I am about to argue for: data extension files pull framework knowledge out
of the query into declarative, shareable tuples.&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt; The seam
already exists; its vocabulary is the limit. Those relations describe data
moving towards danger, because that is the question they were built for. None
of them can say &lt;em&gt;this is a state change&lt;/em&gt;, &lt;em&gt;this is an event publication&lt;/em&gt;, or
&lt;em&gt;this happens inside a transaction&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Which points at what they share. Every one is a rules engine sitting on a model
of your code: bytecode for ArchUnit, a graph for jQAssistant, value flow for
CodeQL, an abstract syntax tree for the linters most engineers have actually
touched.&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt; The models differ; the ceiling does not. Each records what the
code &lt;strong&gt;is&lt;/strong&gt;, and none of them records what the code &lt;strong&gt;means&lt;/strong&gt;. To a syntax tree,
&lt;code&gt;new PutItemCommand({...})&lt;/code&gt; is a &lt;code&gt;NewExpression&lt;/code&gt; whose callee is an
&lt;code&gt;Identifier&lt;/code&gt;. Perfectly accurate. Architecturally useless. Nothing in that
description says a row is about to be written, and no rule, however cleverly
authored, recovers what the model never carried.&lt;/p&gt;
&lt;p&gt;That ceiling explains why dependency rules work and everything else is a fight.
Dependency direction genuinely &lt;em&gt;is&lt;/em&gt; structural, right there in the imports, so
the model holds it and a rule can ask about it. &lt;em&gt;Requests are validated before
they change state&lt;/em&gt; is not. So teams assert the nearest structural proxy instead,
and quietly start treating the proxy as the decision. Testing has been here
already: control for the number of tests in a suite and coverage turns out only
weakly correlated with its ability to catch faults, with stronger coverage
criteria buying no more insight than weaker ones.&lt;sup id="fnref:7"&gt;&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref"&gt;7&lt;/a&gt;&lt;/sup&gt; Coverage was never
the property anyone wanted. It was the part that had a number. You believe you
have protected &lt;em&gt;state changes are validated first&lt;/em&gt;; what has a build status is
&lt;em&gt;the class named &lt;code&gt;OrdersController&lt;/code&gt; does not call the package named
&lt;code&gt;infrastructure&lt;/code&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="has-the-field-already-solved-this"&gt;Has the field already solved this?&lt;/h2&gt;
&lt;p&gt;You might reasonably suspect I am rediscovering something with a thirty-year
literature behind it. Partly, I am.&lt;/p&gt;
&lt;p&gt;Perry and Wolf, in 1992, proposed describing an architecture in three parts —
&lt;strong&gt;elements&lt;/strong&gt;, &lt;strong&gt;form&lt;/strong&gt;, and &lt;strong&gt;rationale&lt;/strong&gt; — the last of them &amp;ldquo;an underlying,
but integral, part of an architecture&amp;rdquo;.&lt;sup id="fnref:8"&gt;&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref"&gt;8&lt;/a&gt;&lt;/sup&gt; Elements and form are
precisely what every tool above extracts. Rationale is where &lt;em&gt;requests are
validated before they change state&lt;/em&gt; lives, and nothing has ever read it. The
same paper names the two ways architecture decays. &lt;strong&gt;Erosion&lt;/strong&gt; is violation:
something contradicts the architecture. &lt;strong&gt;Drift&lt;/strong&gt; is &lt;em&gt;insensitivity&lt;/em&gt; to it, and
it &amp;ldquo;leads more to inadaptability than to disasters&amp;rdquo;. Every architecture test
you run hunts erosion. The two endpoints I opened with are drift, and drift has
never had a build status.&lt;/p&gt;
&lt;p&gt;Software reflexion models then made the move that matters most here: state a
high-level model separately, map extracted source relations onto it, and report
not only where the two converge and diverge, but where the model expects
something the code does not contain.&lt;sup id="fnref:9"&gt;&lt;a href="#fn:9" class="footnote-ref" role="doc-noteref"&gt;9&lt;/a&gt;&lt;/sup&gt; Absence got its own verdict in
1995, and nothing since has moved the boundary: surveys of erosion control keep
concluding that no single strategy suffices,&lt;sup id="fnref:10"&gt;&lt;a href="#fn:10" class="footnote-ref" role="doc-noteref"&gt;10&lt;/a&gt;&lt;/sup&gt; and set side by side, the
established conformance techniques are structural by design.&lt;sup id="fnref:11"&gt;&lt;a href="#fn:11" class="footnote-ref" role="doc-noteref"&gt;11&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;So I part company in a narrow place: the field never made the extracted
relations mean anything beyond structure. A reflexion model can tell you the API
layer talks to the repository layer, not that one path through that conversation
writes to the database without validating first; &amp;ldquo;writes to the database&amp;rdquo; is
not a relation it knows how to extract. The representation half was solved; the
extraction half was left at structure, and that is now the half that matters.&lt;/p&gt;
&lt;h2 id="separate-the-concern-from-the-evidence"&gt;Separate the concern from the evidence&lt;/h2&gt;
&lt;p&gt;The alternative is to write the architecture down in the team&amp;rsquo;s own words first,
and only then go looking for something that can check it.&lt;/p&gt;
&lt;p&gt;Write &lt;em&gt;requests are validated before they change state&lt;/em&gt; down as the thing being
protected, in exactly those words: no class, no package, no framework, and still
true after the team moves off DynamoDB. Then ask a separate question: what would
have to be true for us to believe it? Validation has to apply to every endpoint
that writes, and no path from a request to a write may skip it. Those are the
obligations.&lt;/p&gt;
&lt;p&gt;Only then do you go looking for checks. Evolutionary architecture calls these
&lt;strong&gt;fitness functions&lt;/strong&gt; and deliberately refuses to tie them to one mechanism: a
test, a metric, a monitor or a manual process.&lt;sup id="fnref:12"&gt;&lt;a href="#fn:12" class="footnote-ref" role="doc-noteref"&gt;12&lt;/a&gt;&lt;/sup&gt; The framing is right.
What it needs is a harder requirement on each one: that it states what it
proves, what it is blind to, what it costs, and how often it should run.&lt;/p&gt;
&lt;p&gt;That sounds like bureaucracy until you try it on something harder than
validation. Take the transactional outbox, which exists to stop a service&amp;rsquo;s
internal state and the events other services consume from drifting
apart.&lt;sup id="fnref:13"&gt;&lt;a href="#fn:13" class="footnote-ref" role="doc-noteref"&gt;13&lt;/a&gt;&lt;/sup&gt; What would it take to believe a service really implements it?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A dependency rule proves application code cannot bypass the outbox and publish
directly, not that an outbox event is ever created.&lt;/li&gt;
&lt;li&gt;A code-graph rule can require transactional command methods reaching a known
mutation API to also reach the event API, but only for the mutations and
transaction semantics its model represents. Dirty checking, indirect calls,
branches and reflection walk straight past it.&lt;/li&gt;
&lt;li&gt;A whole-program query can ask whether every relevant path reaches both
operations inside the modelled transaction boundary, at the cost of framework
modelling, analysis time and both kinds of false result.&lt;/li&gt;
&lt;li&gt;An integration test with rollback and failure injection shows state and outbox
rows committing or rolling back together for the cases it exercises, and
enumerates no path it did not run.&lt;/li&gt;
&lt;li&gt;Relay and consumer checks exercise duplicate delivery, idempotency and
ordering, and say nothing about whether the originating write produced the
right event.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Five checks, five models of the world, five different blind spots. Not one of
them is the architecture. Each is evidence about it, and what the team knows is
all five put together, not the colour of whichever one happens to run.&lt;/p&gt;
&lt;p&gt;The reporting has to change too. Today a check is green or red; a team needs
three answers. The thing we wrote down is &lt;strong&gt;supported&lt;/strong&gt; by what we ran,
&lt;strong&gt;contradicted&lt;/strong&gt; by it, or &lt;strong&gt;unassessed&lt;/strong&gt;: nothing we ran can see it at all.
That third is the one almost nothing reports, and the one that matters most.
Reflexion models had it in 1995. If nobody ever wrote a check for &lt;em&gt;no path may
skip validation&lt;/em&gt;, that shows up as silence, and silence looks exactly like
green.&lt;/p&gt;
&lt;p&gt;None of the three tells you whether the architecture was a good idea in the
first place. That judgement stays where it was.&lt;/p&gt;
&lt;h2 id="something-has-to-turn-code-into-intent"&gt;Something has to turn code into intent&lt;/h2&gt;
&lt;p&gt;All of which is fine until you have to write the check. Somewhere, something has
to know that &lt;code&gt;PutItemCommand&lt;/code&gt; is a database write.&lt;/p&gt;
&lt;p&gt;So yes, this moves the difficulty, and that is most of the point. Right now that
knowledge is buried in the conditions of a dozen unrelated rules, owned by
nobody. Pull it out and it becomes a thing with a name, a maintainer, and a
known list of cases it gets wrong. That means giving the rules a different
tree.&lt;/p&gt;
&lt;p&gt;Not an abstract syntax tree — an &lt;strong&gt;intent tree&lt;/strong&gt;, and really a graph once you
add call edges. Where the syntax tree said &lt;code&gt;NewExpression&lt;/code&gt; with an &lt;code&gt;Identifier&lt;/code&gt;
callee, the intent tree says this is a database write, reachable from
&lt;code&gt;POST /orders&lt;/code&gt;, and on one of the two paths that get there it happens before
validation.&lt;/p&gt;
&lt;p&gt;Every linter you have configured already demonstrates the property that matters.
Rules match on node types; they never read source text and never name a vendor.
The messy knowledge — which SDK call means what, how this framework attaches
validation, which branches exist — sits on the far side of the seam, in the
thing that builds the tree. Swapping a parser does not rewrite the rules above
it; swapping DynamoDB for Postgres should not rewrite yours.&lt;/p&gt;
&lt;p&gt;The comparison stops there, and where it stops is the whole difficulty. A syntax
tree is a faithful transform of the source: two parsers disagree only if one of
them is wrong. An intent tree is a judgement. Whether a call counts as a state
change is a decision somebody made, and it can be defensible and still wrong.
That is why the thing building the tree needs an owner and a published list of
what it gets wrong, and why the rules above it have to be able to answer that
they cannot tell.&lt;/p&gt;
&lt;p&gt;&lt;img class="article-figure" src="analysers-and-rules.svg" alt="Code passes through analysers that hold all framework and library knowledge, producing an intent tree of entry points, architectural effects and execution paths. Rules read that tree without naming any technology, and their results answer whether the written-down decision is supported, contradicted, or beyond anything the tooling can see." loading="lazy" decoding="async" title="Analysers take the parser&amp;#39;s place; the intent tree takes the AST&amp;#39;s; the rules never learn what a DynamoDB is."&gt;
&lt;/p&gt;
&lt;p&gt;Building that tree does not remove the difficulty. It concentrates it, and the
places it gathers are not the ones I first expected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Knowing a thing exists is not the same as knowing it applies.&lt;/strong&gt; Establishing
that an application validates requests somewhere is trivial and worth nothing.
To know &lt;code&gt;POST /orders&lt;/code&gt; is covered you have to work out how the framework
attaches validation to that handler, and whether the type it validates carries
any rules at all. Take away either half and the endpoint is wide open, however
much validation the codebase contains.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Execution flow is not the hard part. Getting to it is.&lt;/strong&gt; Path reconstruction
looks like the place this breaks down. It is not: once you have a call graph,
whether one effect precedes another or some path bypasses a required step is
reachability and ordering over edges you already hold. The cost sits one step
earlier. Checked against the edges programs actually took at runtime, static
call graphs for ordinary Java have shown a median &lt;strong&gt;recall&lt;/strong&gt; of 0.884 — roughly
one edge in nine missing, with the analysis working exactly as
designed.&lt;sup id="fnref:14"&gt;&lt;a href="#fn:14" class="footnote-ref" role="doc-noteref"&gt;14&lt;/a&gt;&lt;/sup&gt; The dominant causes were not the reflective calls everyone
worries about. They were objects arriving through native methods, and
invocations the runtime initiates with no matching call site in the analysed
code: framework-registered handlers, the part of a modern service you are most
likely to want a rule about. No rule authoring above pays that down. Recall is
the ceiling on what every rule can honestly claim, because each missing edge is
a path that will never be examined and never be mentioned.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Declining to judge has to be designed in from the start.&lt;/strong&gt; If the tooling
cannot recognise the operations a rule is about, the rule must say it cannot
tell, not that the code complies. Obvious written down; not what tools do by
default, and retrofitting it is far harder, because by then a great many passes
have been issued on the strength of having looked for nothing. Static analysis
settled this better. It has a word for what real tools are. Not sound but
&lt;strong&gt;soundy&lt;/strong&gt;: over-approximate about most of a language, deliberately
under-approximate about a well-understood subset like reflection, &lt;code&gt;eval&lt;/code&gt; and
native calls. The demand that comes with the word is that authors say precisely
which features they do not handle.&lt;sup id="fnref:15"&gt;&lt;a href="#fn:15" class="footnote-ref" role="doc-noteref"&gt;15&lt;/a&gt;&lt;/sup&gt; Architecture testing has never
made that demand of anyone, least of all itself.&lt;/p&gt;
&lt;p&gt;The quietest limitation outranks all three. A tool can say &amp;ldquo;I don&amp;rsquo;t know&amp;rdquo; where
it notices ambiguity, but noticing is itself a capability, and the gaps are
exactly where it does not notice. A call into a library nobody has modelled
produces no evidence. So does an unanalysed &lt;code&gt;catch&lt;/code&gt; block, a handler registered
for later use, a dynamically dispatched call. How often a tool like this says
everything is fine when it simply could not see is the measurement that would
decide whether any of this is worth building, and it is not one I have made.&lt;/p&gt;
&lt;h2 id="putting-it-inside-the-loop"&gt;Putting it inside the loop&lt;/h2&gt;
&lt;p&gt;The reason to care now rather than in 1995 is that the loop has changed shape.&lt;/p&gt;
&lt;p&gt;SWE-agent is a direct precedent for deterministic tool feedback arriving inside
an agent&amp;rsquo;s own turn: its edit command runs a linter and rejects newly
introduced syntax errors rather than deferring them to a later
stage.&lt;sup id="fnref:16"&gt;&lt;a href="#fn:16" class="footnote-ref" role="doc-noteref"&gt;16&lt;/a&gt;&lt;/sup&gt; Execution feedback supplied &lt;em&gt;during&lt;/em&gt; generation beats
no-feedback baselines,&lt;sup id="fnref:17"&gt;&lt;a href="#fn:17" class="footnote-ref" role="doc-noteref"&gt;17&lt;/a&gt;&lt;/sup&gt; and in-execution feedback sidesteps a bias
towards self-generated tests that post-execution feedback picks
up.&lt;sup id="fnref:18"&gt;&lt;a href="#fn:18" class="footnote-ref" role="doc-noteref"&gt;18&lt;/a&gt;&lt;/sup&gt; Every one of those studies concerns functional
correctness, though. I could find no comparative study isolating the effect of
architecture-check feedback on autonomous coding performance, and borrowing the
syntax-linting result to cover it would be precisely the move this article is
arguing against. So this is a reasoned proposal, not a demonstrated result.&lt;/p&gt;
&lt;p&gt;The shape is not mysterious. The agent adds that legacy branch, saves the file,
and before it moves on something works out which written-down decisions the edit
could affect, runs the cheapest checks behind them, and answers in words it can
act on: &lt;em&gt;a database write is now reachable without passing through validation,
on this path&lt;/em&gt;. The agent removes the branch or routes it through validation, and
never needed a person. What it must not do is guess when the answer comes back
unassessed: if nothing can see whether the rule holds, that is a decision for a
human, not a defect for the agent to fix.&lt;/p&gt;
&lt;p&gt;Another model reviewing the architecture is not a substitute. It can help author
a rule, explain a failure, or notice that an encoded model has gone stale. It is
not the repeatable, independent oracle that closes the loop, and treating it as
one puts you back where
&lt;a href="https://jamessawle.dev/writing/agents-write-code-faster-xp-makes-change-safer/"&gt;the XP article&lt;/a&gt;
started.&lt;/p&gt;
&lt;h2 id="the-representation-can-drift-too"&gt;The representation can drift too&lt;/h2&gt;
&lt;p&gt;The strongest objection is that this only creates a second description of the
architecture, which then rots at its own pace. A year after someone writes the
decision down, bulk imports are exempt, the analyser has never been taught about
a new database client, and half the endpoints have moved to a framework nobody
updated the rules for. Four things drifting apart instead of two, and the
dashboard is still green. You have encoded the wrong architecture, more legibly
than before.&lt;/p&gt;
&lt;p&gt;There is a sharper version, and it is the one that bothers me. The rules that
move cleanly between technologies are the easy ones: a forbidden dependency, a
write in the wrong place. The outbox needs two effects correlated inside one
atomic boundary, and a request related to a background processor that runs
later. It is hard to imagine writing that without reaching for the specifics of
one system&amp;rsquo;s transaction mechanism. And if every concern worth checking needs a
bespoke rule, the shared vocabulary bought portability only for the cases that
were never the problem. The bespoke rule becomes the definition of the
architecture, by exactly the route I complained about earlier. It is an
analyser wearing a rule&amp;rsquo;s clothing.&lt;/p&gt;
&lt;p&gt;I do not think that is fatal, but I cannot tell you where the line falls, and
anyone who claims to at this stage is guessing. The separation is most plausible
where evidence is derived from code and the vocabulary is shared, and least
plausible where it is sampled from a running system: a probe measuring
availability during a broker outage is accurate about the runs it watched and
silent about the ones it missed. That is a claim I am making, not a result I am
reporting.&lt;/p&gt;
&lt;p&gt;What bounds the risk is not the schema. It is ownership, versioning, blind spots
published in the way a soundy analysis is supposed to publish them, tests that
prove the analysers can still be wrong, and making the things nothing checks
impossible to overlook. What a team writes down has to sit above the checks and
outlive them. None of it removes the judgement underneath.&lt;/p&gt;
&lt;h2 id="what-a-green-check-actually-establishes"&gt;What a green check actually establishes&lt;/h2&gt;
&lt;p&gt;Whether or not anything like this gets built, there is a question worth taking
back to the architecture tests you already run on Monday.&lt;/p&gt;
&lt;p&gt;A passing architectural check is a claim about a model, not about your system.
It establishes that within the relations the tool represents, using
classifications somebody chose, across the paths it managed to reconstruct, no
counterexample turned up. It is a statement about elements and form; your
rationale was never in scope. Everything outside that boundary is not evidence
of compliance. It is absence of evidence, rendered in the same colour.&lt;/p&gt;
&lt;p&gt;So the question is not whether your architecture tests pass. It is what each one
would have to miss for the architecture to be wrong anyway, and whether anybody
would be able to tell. The two endpoints I opened with were not caught by a
weaker tool than the team had bought. They were caught by no tool at all,
because the thing that distinguished them was never something anyone had asked a
tool to see.&lt;/p&gt;
&lt;p&gt;Erosion was always the easy half. Red builds get fixed; confident silence is the
failure mode worth designing against.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;TNG, ArchUnit, source and user guide, checked at commit
&lt;code&gt;be16e6fd68c2155cfaac08cf87c1098025203a0f&lt;/code&gt;.
&lt;a href="https://github.com/TNG/ArchUnit/tree/be16e6fd68c2155cfaac08cf87c1098025203a0f"&gt;https://github.com/TNG/ArchUnit/tree/be16e6fd68c2155cfaac08cf87c1098025203a0f&lt;/a&gt;
and
&lt;a href="https://github.com/TNG/ArchUnit/blob/be16e6fd68c2155cfaac08cf87c1098025203a0f/docs/userguide/008_The_Library_API.adoc#freezing-arch-rules"&gt;https://github.com/TNG/ArchUnit/blob/be16e6fd68c2155cfaac08cf87c1098025203a0f/docs/userguide/008_The_Library_API.adoc#freezing-arch-rules&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;jQAssistant, &amp;ldquo;Overview,&amp;rdquo; checked at commit
&lt;code&gt;582406a37fa11837e7282a0c415c76812990f579&lt;/code&gt;.
&lt;a href="https://github.com/jQAssistant/jqassistant/blob/582406a37fa11837e7282a0c415c76812990f579/manual/src/main/asciidoc/include/overview.adoc"&gt;https://github.com/jQAssistant/jqassistant/blob/582406a37fa11837e7282a0c415c76812990f579/manual/src/main/asciidoc/include/overview.adoc&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Semgrep, &amp;ldquo;Taint mode overview&amp;rdquo; and &amp;ldquo;Semgrep philosophy,&amp;rdquo; checked at commit
&lt;code&gt;2fa2aa636c2591a029d5c4e9d2b1ed1c4cd93011&lt;/code&gt;.
&lt;a href="https://github.com/semgrep/semgrep-docs/blob/2fa2aa636c2591a029d5c4e9d2b1ed1c4cd93011/docs/writing-rules/data-flow/taint-mode/overview.mdx"&gt;https://github.com/semgrep/semgrep-docs/blob/2fa2aa636c2591a029d5c4e9d2b1ed1c4cd93011/docs/writing-rules/data-flow/taint-mode/overview.mdx&lt;/a&gt;
and
&lt;a href="https://github.com/semgrep/semgrep-docs/blob/2fa2aa636c2591a029d5c4e9d2b1ed1c4cd93011/docs/snippets/contributing/semgrep-philosophy.mdx"&gt;https://github.com/semgrep/semgrep-docs/blob/2fa2aa636c2591a029d5c4e9d2b1ed1c4cd93011/docs/snippets/contributing/semgrep-philosophy.mdx&lt;/a&gt;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;GitHub, &amp;ldquo;Analyzing data flow in JavaScript and TypeScript,&amp;rdquo; CodeQL
documentation, checked at commit
&lt;code&gt;9567f1a22fceaf9f9124e5b4adef879a8aef058e&lt;/code&gt;.
&lt;a href="https://github.com/github/codeql/blob/9567f1a22fceaf9f9124e5b4adef879a8aef058e/docs/codeql/codeql-language-guides/analyzing-data-flow-in-javascript-and-typescript.rst#global-data-flow"&gt;https://github.com/github/codeql/blob/9567f1a22fceaf9f9124e5b4adef879a8aef058e/docs/codeql/codeql-language-guides/analyzing-data-flow-in-javascript-and-typescript.rst#global-data-flow&lt;/a&gt;&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;GitHub, &amp;ldquo;Customizing Library Models for JavaScript,&amp;rdquo; CodeQL documentation,
checked at commit &lt;code&gt;9567f1a22fceaf9f9124e5b4adef879a8aef058e&lt;/code&gt;. The exposed
extensible predicates are &lt;code&gt;sourceModel&lt;/code&gt;, &lt;code&gt;sinkModel&lt;/code&gt;, &lt;code&gt;typeModel&lt;/code&gt;,
&lt;code&gt;summaryModel&lt;/code&gt;, &lt;code&gt;barrierModel&lt;/code&gt; and &lt;code&gt;barrierGuardModel&lt;/code&gt;; the feature is
documented as beta.
&lt;a href="https://github.com/github/codeql/blob/9567f1a22fceaf9f9124e5b4adef879a8aef058e/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst"&gt;https://github.com/github/codeql/blob/9567f1a22fceaf9f9124e5b4adef879a8aef058e/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst&lt;/a&gt;&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;ESLint, &amp;ldquo;Custom Rules&amp;rdquo; and &amp;ldquo;Custom Parsers,&amp;rdquo; ESLint documentation.
&lt;a href="https://eslint.org/docs/latest/extend/custom-rules"&gt;https://eslint.org/docs/latest/extend/custom-rules&lt;/a&gt; and
&lt;a href="https://eslint.org/docs/latest/extend/custom-parsers"&gt;https://eslint.org/docs/latest/extend/custom-parsers&lt;/a&gt;&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;Laura Inozemtseva and Reid Holmes, &amp;ldquo;Coverage Is Not Strongly Correlated
with Test Suite Effectiveness,&amp;rdquo; &lt;em&gt;Proceedings of the 36th International
Conference on Software Engineering&lt;/em&gt; (ICSE 2014), pp. 435–445.
&lt;a href="https://doi.org/10.1145/2568225.2568271"&gt;https://doi.org/10.1145/2568225.2568271&lt;/a&gt;&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:8"&gt;
&lt;p&gt;Dewayne E. Perry and Alexander L. Wolf, &amp;ldquo;Foundations for the Study of
Software Architecture,&amp;rdquo; &lt;em&gt;ACM SIGSOFT Software Engineering Notes&lt;/em&gt; 17(4),
October 1992, pp. 40–52. Quotations verified against the paper text.
&lt;a href="https://doi.org/10.1145/141874.141884"&gt;https://doi.org/10.1145/141874.141884&lt;/a&gt;&amp;#160;&lt;a href="#fnref:8" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:9"&gt;
&lt;p&gt;Gail C. Murphy, David Notkin and Kevin Sullivan, &amp;ldquo;Software Reflexion
Models: Bridging the Gap between Source and High-Level Models,&amp;rdquo;
&lt;em&gt;Proceedings of the 3rd ACM SIGSOFT Symposium on the Foundations of
Software Engineering&lt;/em&gt; (FSE 1995), pp. 18–28
(&lt;a href="https://doi.org/10.1145/222124.222136"&gt;https://doi.org/10.1145/222124.222136&lt;/a&gt;); extended as &amp;ldquo;Software Reflexion
Models: Bridging the Gap between Design and Implementation,&amp;rdquo; &lt;em&gt;IEEE
Transactions on Software Engineering&lt;/em&gt; 27(4), 2001.
&lt;a href="https://doi.org/10.1109/32.917525"&gt;https://doi.org/10.1109/32.917525&lt;/a&gt;&amp;#160;&lt;a href="#fnref:9" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:10"&gt;
&lt;p&gt;Lakshitha de Silva and Dharini Balasubramaniam, &amp;ldquo;Controlling software
architecture erosion: A survey,&amp;rdquo; &lt;em&gt;Journal of Systems and Software&lt;/em&gt; 85(1), 2012. &lt;a href="https://doi.org/10.1016/j.jss.2011.07.036"&gt;https://doi.org/10.1016/j.jss.2011.07.036&lt;/a&gt;&amp;#160;&lt;a href="#fnref:10" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:11"&gt;
&lt;p&gt;Leonardo Passos, Ricardo Terra, Marco Tulio Valente, Renato Diniz and
Nabor das Chagas Mendonça, &amp;ldquo;Static Architecture-Conformance Checking: An
Illustrative Overview,&amp;rdquo; &lt;em&gt;IEEE Software&lt;/em&gt; 27(5), 2010.
&lt;a href="https://doi.org/10.1109/MS.2009.117"&gt;https://doi.org/10.1109/MS.2009.117&lt;/a&gt;&amp;#160;&lt;a href="#fnref:11" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:12"&gt;
&lt;p&gt;Neal Ford, Rebecca Parsons, Patrick Kua and Pramod Sadalage, &lt;em&gt;Building
Evolutionary Architectures: Automated Software Governance&lt;/em&gt;, 2nd edition,
O&amp;rsquo;Reilly Media, 2022. ISBN 978-1-492-09754-9.&amp;#160;&lt;a href="#fnref:12" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:13"&gt;
&lt;p&gt;Debezium, &amp;ldquo;Outbox Event Router,&amp;rdquo; checked at commit
&lt;code&gt;1e05a09e98d948485df11a8e9a791d8dabfe18d1&lt;/code&gt;.
&lt;a href="https://github.com/debezium/debezium/blob/1e05a09e98d948485df11a8e9a791d8dabfe18d1/documentation/modules/ROOT/pages/transformations/outbox-event-router.adoc"&gt;https://github.com/debezium/debezium/blob/1e05a09e98d948485df11a8e9a791d8dabfe18d1/documentation/modules/ROOT/pages/transformations/outbox-event-router.adoc&lt;/a&gt;&amp;#160;&lt;a href="#fnref:13" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:14"&gt;
&lt;p&gt;Li Sui, Jens Dietrich, Amjed Tahir and George Fourtounis, &amp;ldquo;On the Recall of
Static Call Graph Construction in Practice,&amp;rdquo; &lt;em&gt;Proceedings of the ACM/IEEE
42nd International Conference on Software Engineering&lt;/em&gt; (ICSE 2020).
Median recall 0.884, rising to a median of 0.935 with state-of-the-art
dynamic-feature support at a substantial performance cost.
&lt;a href="https://doi.org/10.1145/3377811.3380441"&gt;https://doi.org/10.1145/3377811.3380441&lt;/a&gt;&amp;#160;&lt;a href="#fnref:14" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15"&gt;
&lt;p&gt;Benjamin Livshits, Manu Sridharan, Yannis Smaragdakis, Ondřej Lhoták,
J. Nelson Amaral, Bor-Yuh Evan Chang, Samuel Z. Guyer, Uday P. Khedker,
Anders Møller and Dimitrios Vardoulakis, &amp;ldquo;In Defense of Soundiness: A
Manifesto,&amp;rdquo; &lt;em&gt;Communications of the ACM&lt;/em&gt; 58(2), February 2015, pp. 44–46.
&lt;a href="https://doi.org/10.1145/2644805"&gt;https://doi.org/10.1145/2644805&lt;/a&gt;&amp;#160;&lt;a href="#fnref:15" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:16"&gt;
&lt;p&gt;SWE-agent, &amp;ldquo;Agent-Computer Interface,&amp;rdquo; checked at commit
&lt;code&gt;3ea751c087f32b16e039a2233dd6eefecef325d5&lt;/code&gt;.
&lt;a href="https://github.com/SWE-agent/SWE-agent/blob/3ea751c087f32b16e039a2233dd6eefecef325d5/docs/background/aci.md"&gt;https://github.com/SWE-agent/SWE-agent/blob/3ea751c087f32b16e039a2233dd6eefecef325d5/docs/background/aci.md&lt;/a&gt;&amp;#160;&lt;a href="#fnref:16" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:17"&gt;
&lt;p&gt;Xinyun Chen, Maxwell Lin, Nathanael Schärli and Denny Zhou, &amp;ldquo;Teaching
Large Language Models to Self-Debug,&amp;rdquo; arXiv:2304.05128, 2023.
&lt;a href="https://arxiv.org/abs/2304.05128"&gt;https://arxiv.org/abs/2304.05128&lt;/a&gt;&amp;#160;&lt;a href="#fnref:17" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:18"&gt;
&lt;p&gt;&amp;ldquo;Revisit Self-Debugging with Self-Generated Tests for Code Generation,&amp;rdquo;
arXiv:2501.12793, 2025. &lt;a href="https://arxiv.org/abs/2501.12793"&gt;https://arxiv.org/abs/2501.12793&lt;/a&gt;&amp;#160;&lt;a href="#fnref:18" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item></channel></rss>