Architecture decision records
An ADR captures one significant decision: the context, the options considered, what we chose, and the consequences. We use a short version of MADR (see template.md).
How we use them
Section titled “How we use them”- When to write one: a decision is hard to reverse, shapes public API, or will be questioned later. Small, reversible choices do not need one.
- Numbering: sequential,
NNNN-short-title.md. Numbers are never reused. - Status:
Proposed→Accepted(orRejected). A later decision that reverses one is a new ADR. It marks the old oneSuperseded by NNNNand links back. To narrow part of it, the new record quotes the sentences it narrows, and the old record gains a line under its status,Narrowed by NNNN (Decision k); nothing else in it changes. We never delete or rewrite an accepted ADR’s decision. - Who decides: anyone can propose. The maintainer accepts.
Questions not yet decided are in open-questions.md.
| # | Decision | Status |
|---|---|---|
| 0001 | Record architecture decisions | Accepted |
| 0002 | The Django app is the unit of deployment | Accepted |
| 0003 | Apps communicate only through contracts and connectors | Accepted |
| 0004 | Each app owns its data | Accepted |
| 0005 | Small required core; no bundled shared-service apps | Proposed |
| 0006 | Contracts are typed and published as OpenAPI | Accepted |
| 0007 | The in-process transport serializes, like the network ones | Accepted |
| 0008 | Contract errors are typed and identical in both modes | Accepted |
| 0009 | One database alias per app | Proposed |
| 0010 | Pluggable principal resolution in core | Accepted |
| 0011 | django-ninja for contract endpoints | Proposed |
| 0012 | Supported Python and Django versions | Accepted |
| 0013 | License: BSD-3-Clause | Accepted |
| 0014 | Connector transports are pluggable | Proposed |
| 0015 | Specification first; django-neolith is the reference implementation | Accepted |
| 0016 | An app may run attached and detached at the same time | Proposed |
| 0017 | The call context is explicit at the action and ambient for propagation | Accepted |
| 0018 | Requirement IDs and spec-to-test traceability | Accepted |
| 0019 | Wire encodings of the portable forms | Accepted |
| 0020 | A portable form whose wire spelling differs from Pydantic’s default is a neolith Annotated type | Accepted |
| 0021 | An app declares itself in class attributes of its NeolithAppConfig, with contracts named by their declaration classes |
Proposed |
| 0022 | The app host builds settings from declarations, checks at app loading, and boots when a handler is built | Proposed |
| 0023 | A request’s principal comes from ordered backends that declare their request inputs, and a Django user is named by a deployer-given issuer and its primary key | Proposed |