Step 12: Keep the architecture alive
What you get from this step
Section titled “What you get from this step”A routine that keeps three things in line while the system changes: your decisions, your documentation and the structure of your code.
Without such a routine, the built system drifts away from the intended design. Small, unplanned changes pile up. This drift is called Architecture Decay. Left alone, it can end in a Big Ball of Mud, a system whose structure comes from quick fixes instead of design.
After this step you can:
- see when the code and the description disagree,
- see which decisions are outdated,
- decide what to fix now and what to write down as debt.
What you need first
Section titled “What you need first”- A description of the intended architecture. At minimum, this is the building block view, which shows the system’s static parts. See Component View.
- A small log of recorded decisions. See Architectural Decision Record.
- A list of known shortcuts and weak spots. See Technical Debt.
- Documentation and code in version control. See Documentation as Code.
- Automated tests for the code you plan to change. They act as a safety net for refactoring. See Refactoring.
If something is missing
Section titled “If something is missing”- No description of the intended architecture. Every system has an architecture, even an unplanned one. Without a stated intention, you cannot compare it with the code. Write down the intended structure first. See Implicit Architecture and Accidental Architecture.
- No decision records. Do not record everything. Start with the decisions that are hard or costly to change. See Architecturally Significant Decision.
- No tests. Refactoring needs a safety net, so add tests around the code you want to change before you restructure it. See Refactoring.
- A legacy code base with many rule violations. You do not have to fix them all at once. Freeze the existing ones so that no new ones get in. See Architecturally Evident Coding Style.
How to do it
Section titled “How to do it”- State the intended architecture and name an owner. Write down what the structure should be, so you can compare it with what is built. Name one person who is responsible for the amount and quality of the documentation. See Implicit Architecture and Guidelines for Documentation.
- Keep documentation next to the code. Use plain-text files in version control. Propose changes through pull requests, so reviewers see them. Build the published documents automatically. Store diagrams as text too, so you can compare versions. See Documentation as Code, Self-Documentation and Diagrams as Code.
- Generate what the code can say. Write by hand what it cannot. Generate interface documentation from the source. Write down the reasons behind technology choices yourself. Code alone is not full documentation. See Self-Documentation and Motivation for Architecture Documentation.
- Update decisions when the context changes. Give each decision record a trigger for review, such as a change that would reopen the question. When a new decision replaces an old one, mark the old one as superseded and point to its replacement. See Architectural Decision-Making Process, Architectural Decision Management Cycle and Decision Dependencies.
- Mark planned changes in the description. Show planned decisions as proposed or accepted, and replaced ones as deprecated or superseded. Show temporary transition parts and plan their removal. See AS-IS and TO-BE View.
- Make the rules visible in the code and check them automatically. Express layers and naming rules through package layout and names. Run architecture rules as automated tests in every build. See Architecturally Evident Coding Style.
- Detect divergence. Run conformance checks that compare the intended and the built structure. Use static analysis to find code smells, which are surface signs of deeper design problems. You can also measure components against the Main Sequence, but do not add empty interfaces just to improve the score. See Architecture Decay and Code Smell.
- Record gaps as technical debt. List each gap between intended and built structure, with a priority. Mark deliberate shortcuts as knowingly accepted and add a plan to repay them. See Technical Debt.
- Refactor continuously, in small steps. Refactoring changes the inside of the code without changing what it does. Alternate adding features with consolidation. Integrate often, so a working version always exists. See Refactoring, Keep It Working and Big Ball of Mud.
- Expect the solution to move. Note which decisions you deliberately postponed and when the responsible moment to decide arrives. Separate reversible decisions from hard-to-change ones. See Solution as Moving Target and Last Responsible Moment.
Example: ClinicBook
Section titled “Example: ClinicBook”ClinicBook has a small team and a fixed launch date. Its team expects shortcuts, so they set up the routine early.
Intended structure. The building block view shows the web and mobile apps, a booking part, a calendar part, a reminder part and one adapter for each practice-management software vendor. The team stores this text in the same repository as the code. A named team member owns it. Pull requests that change the structure must also change the description.
Decisions. The team records “one adapter per vendor” as a decision, because it is costly to change and affects integration with the legacy systems. Its record says what would trigger a review: a new vendor whose software does not fit the adapter approach. Later the team replaces its SMS provider. They write a new record, mark the old one as superseded and link it to the new one.
Rules in the code. Two rules are written as automated tests in the build:
- Only adapters talk to vendor software.
- Only the booking and calendar parts touch patient data.
The second rule supports the privacy and security goal. A change that breaks a rule fails the build.
Shortcut before launch. Under time pressure, a developer lets the reminder part read the calendar tables directly. The rule test fails. The team decides to accept this on purpose for launch. They record it as technical debt, mark it as knowingly accepted and plan a refactoring after launch. They freeze this one violation so no others join it.
After launch. Each sprint reserves time for consolidation. The team refactors the reminder part behind a clean interface, with tests as a safety net. They then remove the debt entry and the frozen violation. Before the next morning-peak review, they check whether the decisions about availability still match how patients use the system.
Done when
Section titled “Done when”- The intended architecture is written down, kept in version control with the code, and has a named owner. See Documentation as Code.
- Decision records exist for the significant decisions, each with its reasons, and replaced ones are marked as superseded. See Architectural Decision Record.
- Architecture rules run as automated tests in the build, and old violations are frozen. See Architecturally Evident Coding Style.
- The team has a way to detect divergence between intended and built structure. See Architecture Decay.
- Known gaps are listed as technical debt, with priorities and repayment plans. See Technical Debt.
- Refactoring is ongoing work, protected by tests and done in small steps. See Refactoring.
Learn more
Section titled “Learn more”- Architecture Decay
- Implicit Architecture
- Accidental Architecture
- Big Ball of Mud
- Documentation as Code
- Self-Documentation
- Diagrams as Code
- Guidelines for Documentation
- Motivation for Architecture Documentation
- Architectural Decision Management Cycle
- Architectural Decision-Making Process
- Decision Dependencies
- AS-IS and TO-BE View
- Architecturally Evident Coding Style
- Code Smell
- Main Sequence
- Technical Debt
- Refactoring
- Keep It Working
- Incrementality
- Solution as Moving Target
- Last Responsible Moment