Clean Architecture
Global technical layers can fragment a change. A folder layout can split one behavior across the application. BSA applies dependency discipline inside a recognizable domain owner.
Code that changes together belongs together. An architecture heuristic for a smaller search space, explicit ownership, and predictable dependencies.
It started with Branch and Bound optimization technique. The spark for an architecture that keeps the search small as your codebase grows.
Built for developers. Legible to coding agents.
Related architecture approaches
Global technical layers can fragment a change. A folder layout can split one behavior across the application. BSA applies dependency discipline inside a recognizable domain owner.
Expose the domain. Then check the connections. Vertical slices keep a use case together; Screaming Architecture makes the application’s purpose visible. Neither organizing principle, on its own, verifies which components may connect. BSA makes those connections explicit and checks them against actual imports.
Identify the module that owns the behavior you want to change.
Keep local behavior with its owner. Put a shared domain concept at the smallest common owner.
Use the intended audience surface and a declared import direction. Avoid cycles.
Run focused tests and boundary checks to make sure the surfaces hold.
Name boundaries after the business. Keep each concept, its rules, and related implementation with the owner of that concept.
Say who may use each public surface and in which direction. Put coordination at the smallest owner that can connect the parts.
Check actual imports against the declared connections. Reject private access and back-edges; test behavior through supported surfaces.
Each boundary owns a coherent responsibility and may contain smaller boundaries. Imports form a directed graph: modules can share anchors and connect different boundaries through declared surfaces, without cycles.
Meet Meridian, a fictional shipping company. Shipping exposes shipment.py and deliveries/ directly; deliveries own assignments and delays. Assignments use fleet’s public vehicle definitions. Fleet never imports shipping.
meridian/company boundaryapi.pyentry adaptercli.pyentry adapterworker.pyentry adapterfleet/boundary_commands/private branchcommands.pypublic surfacedeps.pycoordinatorhttp_routes.pyHTTP surfacevehicle.pyanchorshipping/boundary_commands/private branchcommands.pypublic surfacedeliveries/sub-boundaryassignments/sub-boundary_commands/private branchassignment.pyanchorcommands.pypublic surfacecommands.pypublic surfacedelays/sub-boundary_commands/private branchcommands.pypublic surfacedelay_report.pyanchordelivery.pyanchordeps.pycoordinatorhttp_routes.pyHTTP surfaceshipment.pyanchorShipping → fleet is the declared direction. Assignments own the relationship; deps.py composes it. Arrows are source imports, not execution.
Entry points together; domain contents alphabetical, with _commands/ first. Anchors keep values, contracts, and implementations together. Package __init__.py exports and shared HTTP support are omitted.
Use these roles when needed. A small boundary can stay in one module; private branches and separate coordinators are optional.
Child conceptparent anchor
from meridian.shipping import ShipmentIdCoordinatorsub-boundary surface
from meridian.shipping.deliveries.delays import DelayReportsOutside callerconsumer surface
from meridian.shipping.commands import RegisterShipmentA surface exports selected symbols to a named audience. It does not make the boundary's descendants public.
For coding agents
Ownership, composition, and import rules for your Python coding agent. Start with a blank Python project and a prompt for your own business requirements.
A detailed architecture guide and working reference applications.
Start with one boundary. Keep the next change local.