Use when reviewing architectural concerns: module boundaries, dependency direction, layer discipline, bounded-context isolation, ADR coverage, premature abstraction, and structural fitness for change
How this skill is triggered — by the user, by Claude, or both
Slash command
/software-leverage-points:architectureThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Architecture is the set of structural decisions that shape what a system can become. It is the part hardest to change after the fact, so the part most worth getting right deliberately. Architecture is not "the diagram"; it is the constraint regime that determines whether the next change is local or global.
Architecture is the set of structural decisions that shape what a system can become. It is the part hardest to change after the fact, so the part most worth getting right deliberately. Architecture is not "the diagram"; it is the constraint regime that determines whether the next change is local or global.
The scope of architecture is requirements plus architectural characteristics: the functional behavior the system delivers and the critical capabilities (reliability, maintainability, scalability, security, auditability, performance, modularity, …) it must preserve while delivering it. Both shape structure.
Core principle: The strategy of architecture is to leave as many options open for as long as possible. Dependencies point toward stability; modules hide secrets; decisions leave receipts; structural seams match team and agent boundaries; architectural characteristics are named and mechanically verified. Each named principle below is a way of preserving the option to change.
A module is a unit of decomposition organized around something it hides from its callers: a data structure, an algorithm choice, a third-party API. The interface is the contract; the secret is whatever can change behind it without breaking callers.
A module that hides nothing (a "junk drawer" utils/, common/, core/) is not a module; it is a coupling point.
Dependencies point from volatile details toward stable abstractions. High-level modules (use cases, domain logic) do not import low-level concrete types (database drivers, HTTP clients, filesystem helpers); they depend on abstractions they own.
When the arrow points the other way, every detail change ripples upward through the stable core.
Validation, pricing, authorization, and workflow rules are domain concerns; they do not live in HTTP handlers, ORM models, or view templates. Layering exists so each module changes for one reason. When business logic lives in the transport layer, it cannot be reused, tested without spinning the layer up, or moved without rewriting it.
Multiple valid layering schools exist: clean architecture, hexagonal (ports and adapters), onion, vertical-slice. The principle is: pick one and apply it consistently. The red flag is mixing schools without a stated rule, not "did not pick hexagonal."
Module A imports B and B imports A (directly or transitively) means A and B are one module pretending to be two. Neither can be reasoned about, tested, or replaced in isolation, and the build graph stops being a graph.
The Acyclic Dependencies Principle is non-negotiable in any layering scheme.
The cheapest abstraction is the one extracted from three concrete cases. Speculative abstractions ("for future flexibility") locked in from one or two cases lock in the wrong shape; future changes route around the abstraction rather than through it, accumulating accidental complexity.
This principle scopes to abstraction extraction, not to constants or configuration. A constant defined once is fine; an interface defined for one caller is not.
This principle scopes to systems large enough to have multiple bounded contexts. A single-context POC inherits a weaker form: keep the option to split later by not coupling internal models to transport.
When the scope applies: each context owns its data and its model; cross-context coupling (importing internal types, querying another context's tables, sharing mutable in-process objects) collapses two contexts into one with two names. Integration between contexts goes through explicit messaging or APIs.
Architectural decisions evaporate the moment they are made unless captured. ADRs record context, decision, and consequences at the moment of choice, so the next contributor (or agent) can tell deliberate from accidental.
ADR coverage is a precondition for the system to remain editable across team changes; without it, every load-bearing decision becomes a candidate for relitigation.
Code that implements a load-bearing decision back-references the ADR in a top-of-file comment (or equivalent locality marker), using the exact ADR filename slug (e.g., 0042-postgres-to-cockroach) so the reference is greppable. During a refactor, grep -r 0042-postgres-to-cockroach returns every file implementing that decision, which is the only mechanically-reliable way to find them. Free-text back-references ("see the database ADR") cannot be grepped, drift silently, and lose the receipt. ADRs without back-references rot quietly; back-references without ADRs are unverifiable claims.
"Any organization that designs a system will produce a design whose structure mirrors the organization's communication structure" (Conway, 1968). The corollary is the design imperative: pick the team and collaboration shape you want, and make the architectural seams match it. For small, parallel, cross-functional teams (and increasingly, parallel agents), seams must be drawn so each team or agent can work in isolation without serializing on shared state.
Two tactical realizations recur:
Both are sketched here; deep-dives live in dedicated companion docs: event sourcing, event modeling, and CQRS in event-sourcing/README.md; vertical-slice patterns in vertical-slice/README.md.
When seams do not match the team boundaries, every feature crosses a coordination boundary; teams (and agents) serialize on each other and the speed of change collapses regardless of how clean the dependency graph looks.
This principle, named and developed by Neal Ford, Rebecca Parsons, Patrick Kua, and Pramod Sadalage in Building Evolutionary Architectures, treats architectural decisions the way the rest of the codebase treats domain logic: as claims that need automated checks to stay true under change.
Architectural characteristics are the named non-functional capabilities the system must preserve: reliability, scalability, security, auditability, performance, modularity, evolvability, accessibility, and others appropriate to the domain. They are not universal; they are picked per system and trade off against each other. Naming them is the first step; an unnamed characteristic cannot be designed for, and can only erode silently.
Fitness functions are automated checks that validate architectural decisions. They are to architectural characteristics what unit tests are to domain integrity. Each named characteristic gets one or more mechanical checks that verify it is preserved as the code evolves: architecture tests for module-graph and import-direction rules; code metrics for coupling, instability, abstractness, distance from main sequence, cyclomatic and cognitive complexity; security scans for the security characteristic; performance budgets and load tests for performance; chaos experiments for resilience; SLO checks for reliability. The system-wide fitness function is the union of the individual ones, used to evaluate trade-offs when a proposed change shifts one characteristic at the expense of another.
Architects measure outcomes (the objective fitness measure), not implementations. Without fitness functions, every architectural rule decays to folklore between code reviews; with them, rules are checked on every commit and the architecture evolves under guard rather than under hope. In an agentic codebase, where humans review a smaller fraction of changes, this is closer to a precondition than a nice-to-have.
For the deep-dive (metric vocabulary, fitness function categories, system-wide composition, pipeline integration, anti-patterns, maturity progression), see fitness-functions/README.md.
Cross-reference: the types skill carries the same fitness-function pattern applied to type-system invariants the checker cannot enforce alone (silent Any leakage, untyped dict returns, escape-hatch sprawl). The dependencies skill carries the pattern applied to supply-chain commitments (zero-dep entry points, transitive depth caps, package-count budgets); see dependencies/dependency-minimization/README.md for the dependency-specific instances. The security skill carries the pattern applied to committed security rules (no eval outside sandbox, no string-concat SQL, no plaintext secrets in logs). The environments skill carries the pattern applied to committed parity rules (runtime-version drift, image-content audit, dependency-tree drift, seed-data minimums).
common/, utils/, core/ package imported almost everywhere and growing unboundedly (god module)| Excuse | Reality |
|---|---|
| "We'll write the ADR later" | Later doesn't come; the rationale lives in a Slack thread that expires |
| "It's just a small cycle" | A cycle means the modules are one module; reasoning collapses regardless of size |
| "Importing the driver directly is simpler" | It is, until the driver changes and every use case has to change with it |
| "Business logic in the controller is fine for now" | Now becomes the next five years; the rule duplicates the next time it is needed |
| "We need this abstraction for future flexibility" | Future flexibility from one case is a guess; from three cases it is a pattern |
| "Bounded contexts are over-engineering" | They are, until two teams ship conflicting changes to the same model |
| "utils.py is just a place for shared helpers" | It is the de facto coupling point; every change to it is a change to everything |
"The ADR is in docs/adrs/; the code doesn't need to mention it" | Receipts unreachable from code rot; the back-reference is what keeps the rationale alive at the point of edit |
| "We don't need to think about team shape, the architecture is clean" | A clean dependency graph that crosses team boundaries on every feature still serializes the teams; Conway wins |
| "Vertical slices duplicate code" | Some duplication across slices is the price of independent change; premature DRY across slices is the bug |
| "Reads and writes can share one model" | Until they can't; CQRS is cheap before scale forces it and expensive after |
| "We have non-functional requirements, we just haven't written them down" | Unnamed characteristics aren't designed for; they degrade silently |
| "The architecture rules are in the wiki / code review checklist" | Rules unenforced rot; mechanize them as fitness functions or accept they will erode |
✅ Use case imports a Repository interface it owns; adapter implements it
❌ Use case imports psycopg2.connect directly
✅ HTTP handler: parse request → call use case → format response
❌ HTTP handler: parse, validate business rules, query DB, compute price, format response
✅ docs/adr/0042-switch-to-cockroach.md (context, decision, consequences)
❌ Database swap shipped with diff only; rationale in a stale Slack thread
✅ Module graph: domain ← application ← interface; no back-edges
❌ domain imports application for a "shared helper"; cycle established
✅ Abstraction extracted from three concrete cases that already exist
❌ Abstraction introduced for "future flexibility" with one caller
✅ Context A talks to Context B via published API or message
❌ Context A queries Context B's tables directly
✅ payments/processor.py: top-of-file comment names ADR by exact slug `0042-idempotent-charges` (greppable across the repo)
❌ payments/processor.py: comment says "see the idempotency ADR"; not greppable, not reliably findable during refactor
✅ Slice payments/refunds/* owns its routes, use case, repository; no slice imports another slice's internals
❌ refunds/use_case.py imports orders/internal/calculator.py; slices coupled through internals
✅ Write side (commands) goes through aggregate; read side (queries) hits a denormalized projection
❌ Single ORM model serves both list pages and high-throughput command path; one constrains the other
✅ Named characteristics (reliability, security, performance) each have at least one fitness function in CI
❌ "Non-functional requirements" listed in the wiki; nothing in CI verifies them
✅ dependency-cruiser / import-linter / ArchUnit rule fails the build on a back-edge
❌ Architecture rules live only in the code review checklist
Architecture is the part of a system that resists change most. Get it wrong early and every subsequent change pays a tax; get it right and the system absorbs new requirements without rewrites. The principles above are not preferences; they are the constraints that keep change local and keep the named architectural characteristics intact under evolution.
The outcomes architecture serves are, in Kleppmann's framing, reliability, maintainability, and scalability, plus whichever additional characteristics the system has named (security, auditability, performance, evolvability, …). Each of these is achievable only if the structural decisions support it; each is verifiable only if the rules are mechanized.
Without architectural discipline:
With architectural discipline:
Soft sketch; not a checklist. Where appropriate is shaped by the target's maturity.
These go stale fast; the date is the "as-of." Verify currency before adopting. The principles above outlast specific tools; if a tool here is no longer maintained, the patterns transfer to its replacement.
dependency-cruiser (JS/TS), import-linter (Python), archtest (Go).gitleaks for secrets; Google's osv-scanner for dependency vulnerabilities (SCA); Semgrep and GitHub CodeQL for SAST; OWASP ZAP for DAST.adr-tools CLI, log4brains, MADR template, plain markdown in docs/adr/.dependency-cruiser --output-type dot, pydeps, cargo-modules, go-callvis.This skill is maintained at: https://github.com/syntropic137/software-leverage-points/blob/main/skills/architecture/SKILL.md
To improve it, edit the file directly and follow the chassis discipline in maintaining-software-leverage-points: regenerate catalogs, run just qa, then commit.
Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub syntropic137/software-leverage-points --plugin software-leverage-points