Introduction
In our previous lesson, we established the purpose and structure of Architecture Decision Records (ADRs), framing them as an essential tool for preserving the "why" behind significant architectural choices. We saw that a well-structured ADR contains the context, decision, alternatives, and consequences, serving as a historical log of your system's evolution.
Now, we will transition from theory to practice. This lesson is a hands-on workshop focused on your current learning objective: to write an effective ADR for a cross-cutting architectural change. A cross-cutting change is one that impacts multiple teams or services, making clear, rigorous documentation not just a good practice, but a necessity for maintaining system coherence and team alignment. We will work through a realistic scenario, focusing on the thought process required to populate each section of an ADR with high-quality, actionable information.
What Makes an ADR "Effective"?
Before we begin writing, it's important to distinguish between simply filling out a template and crafting an effective ADR. An effective ADR is a tool for thinking and communication, not just a bureaucratic artifact. The quality of an ADR is measured by its clarity, the rigor of its analysis, and its utility to a future reader who has none of the original context.
Conversely, ineffective ADRs often fall into common traps. A great resource to keep in mind is the "Common Mistakes" section from the article "Architecture Decision Records: Why and How," which we can use to define a set of guiding principles.
Architecture Decision Records: Why and How - Tek42
This section highlights the most common pitfalls when creating ADRs. Understanding these will help you focus on what truly matters when you write your own.
Please read the section titled Common Mistakes. As you read, reflect on how each mistake undermines the core purpose of an ADR.
From this reading, we can distill five principles for writing effective ADRs:
- Write it during the decision, not after. The ADR is a tool to clarify your thinking and structure your analysis. It should not be a justification written after the fact.
- Focus on the signal, not the noise. Be concise. The goal is to document the rationale, not every implementation detail. Link to external research or lengthy analysis if needed.
- Document rejected alternatives rigorously. This is often the most valuable part of the ADR. It saves future teams from re-litigating the same debates and provides a starting point if the context changes.
- Embrace the trade-offs. No decision is perfect. Honestly documenting the negative consequences and risks builds trust and helps future teams understand the operational reality of the choice.
- Remember it's a record, not a rule. An ADR documents a decision made at a point in time. When the context changes, the correct action is not to blindly follow the old ADR, but to write a new one that supersedes it.
With these principles in mind, let's tackle a practical scenario.
Scenario: Taming the Logging Chaos
Imagine you are a principal engineer at "ConnectiCorp," a company that has experienced rapid growth. The platform consists of over 50 microservices, developed by eight different stream-aligned teams. This growth has led to a significant operational problem: inconsistent logging.
- The Problem: Each team has implemented logging in its own way. Some use plain text, others use key-value pairs, and a few have adopted different JSON structures. There is no standard for log levels, timestamps, or service identification.
- The Impact: When an incident occurs, on-call engineers struggle to correlate events across services. Finding the root cause of a bug that spans multiple services is a time-consuming forensic exercise. The Mean Time To Resolution (MTTR) for cross-service incidents has climbed to over three hours. The central observability platform (e.g., Splunk or Datadog) is expensive, and much of the ingested log data is low-value "noise."
- The Mandate: The CTO has asked you to lead the effort to define and document a new, unified logging strategy that all teams must adopt. Your first task is to create an ADR that outlines the decision.
This is a classic cross-cutting concern. The decision will affect every development team, and it has clear implications for cost, reliability, and developer productivity—all topics that are highly relevant to an architect's role.
Authoring the ADR: A Step-by-Step Guide
We will now write the ADR for this logging strategy, section by section. We'll use a standard, widely-adopted template as described in the resources.
1. Title and Status
First, we need a clear, numbered title. The title should be a short noun phrase describing the decision. We'll also set the initial status.
ADR-009: Adopt a Standardized Structured Logging Format
Status: Proposed
We start with Proposed because this ADR is a draft to be discussed and refined with stakeholders from the various teams.
2. Context
This section sets the scene. It must describe the problem, the business and technical forces at play, and any constraints. A future reader should be able to understand why a decision was necessary.
Context
Our platform has grown to over 50 microservices maintained by 8 teams. This rapid, decentralized development has resulted in a lack of logging standardization. Log formats vary between services (plain text, key-value, unstructured JSON), and there is no consistent schema for critical metadata such as
service_name,trace_id, orcustomer_id.This inconsistency directly impacts system reliability and operational efficiency. The primary consequences are:
- High Mean Time To Resolution (MTTR): Correlating logs across services during an incident is manual and difficult, with our average MTTR for P1/P2 incidents involving multiple services exceeding 3 hours.
- Ineffective Alerting: It is impossible to build reliable, automated alerts based on log content across the platform.
- Wasted Engineering Time: Developers spend significant time on forensic debugging rather than feature development.
- High Observability Costs: A significant portion of our logging budget is spent ingesting and storing unstructured, low-signal logs.
A decision is required to establish a unified logging strategy to improve system observability, reduce operational overhead, and enable teams to debug more effectively in a distributed environment. The solution must be adoptable by teams using Go, Python, and Node.js.
This context is effective because it is specific, data-driven (mentioning MTTR), and links the technical problem directly to business pain points (cost, efficiency, reliability).
3. Decision
This is the clear, unambiguous statement of what will be done. Use active voice.
Decision
We will adopt a standardized, structured JSON format for all application logs written to
stdout. All logs must conform to a common base schema.The base schema must include the following fields:
timestamp: ISO 8601 format with UTC timezone.log_level: (e.g.,INFO,WARN,ERROR).service_name: The official name of the service.trace_id: The correlation ID for distributed tracing.message: A human-readable log message.Service-specific, contextual data should be nested within a
payloadobject. Example:{ "timestamp": "2023-10-27T10:00:00.123Z", "log_level": "INFO", "service_name": "order-service", "trace_id": "abc-123-def-456", "message": "Order processed successfully", "payload": { "order_id": "ord-98765", "customer_id": "cust-4321" } }We will provide recommended libraries for Go, Python, and Node.js to facilitate adoption but will not mandate a specific framework. Compliance will be enforced via CI checks.
This decision is strong because it is precise. It doesn't just say "use structured logging"; it defines the format, the transport (stdout), and the core fields.
4. Alternatives Considered
This section is vital for demonstrating rigor and saving future time.
Alternatives Considered
- Do Nothing (Continue with current approach):
- Rationale: This requires no new work.
- Why Rejected: This is not a viable option. As the number of services grows, the operational problems outlined in the context will compound, leading to a critical decline in system reliability and an unsustainable increase in operational costs.
- Mandate a single logging framework across all stacks (e.g., a specific library for Go, Python, and Node.js):
- Rationale: This would provide maximum consistency in both output and implementation.
- Why Rejected: This approach is too prescriptive and would create significant friction. Teams value the autonomy to choose tools best suited for their stack. It could also lead to challenges if the mandated library falls behind in one language ecosystem. It prioritizes absolute uniformity over team autonomy and pragmatism.
- Use a logging sidecar to process and forward logs:
- Rationale: Application teams could continue to log in any format, and a sidecar container would be responsible for parsing, structuring, and forwarding the logs.
- Why Rejected: While this decouples the application from the logging format, it introduces significant operational complexity (deploying and managing another container per pod) and performance overhead. It also fails to solve the root problem: applications are not emitting structured, context-rich data. Parsing unstructured text is brittle and less reliable than generating structured logs at the source.
5. Consequences
Here, you must be honest about the trade-offs. What gets better, what gets harder, and what are the new risks?
Consequences
Positive:
- Logs will be machine-readable and searchable by default, dramatically reducing debugging time and MTTR.
- We can build reliable, platform-wide dashboards and alerts based on log content (e.g., alert on a spike in
ERRORlevel logs for a specific customer).- The inclusion of
trace_idenables seamless correlation between logs and distributed traces.- Onboarding new engineers will be simpler as the logging standard is documented and consistent.
Negative:
- All teams will incur an upfront cost to refactor their existing logging implementation to conform to the new standard. This will require coordinated effort across all 8 teams.
- There may be a minor performance overhead associated with serializing logs to JSON compared to plain text, though this is expected to be negligible for our use cases.
- The standard adds a layer of governance that teams must adhere to, slightly reducing their autonomy in this specific area.
Risks:
- Adoption Risk: Teams may be slow to adopt the standard or may implement it incorrectly. This will be mitigated by providing clear documentation, reference implementations, and automated CI checks to enforce compliance.
- Schema Evolution: The base schema may need to evolve. Changes will need to be managed carefully to ensure backward compatibility in our log processing pipeline.
This completes our ADR. The final document is a concise but comprehensive record that not only states the decision but also provides the rich context needed to understand it. The overall process, from a high-level requirement to an implemented decision documented in an ADR, can be visualized by the following framework.

Conclusion
In this lesson, we moved from understanding the theory of ADRs to the practical task of authoring one for a significant, cross-cutting architectural change. You have learned that an effective ADR is more than just a filled-in template; it is a carefully crafted document that serves as a tool for rigorous thinking and clear communication.
Key Takeaways:
- Effective ADRs are thinking tools: They should be written during the decision-making process to structure your analysis of the problem, alternatives, and trade-offs.
- Context is king: A well-written context allows future engineers to understand the constraints and forces that shaped the decision.
- Rigorously document alternatives: Explaining why other options were rejected is critical for preventing future debates and providing insight when circumstances change.
- Be honest about consequences: Acknowledging the downsides and risks of a decision demonstrates intellectual honesty and prepares the organization for the real-world impact.
Preview of the Next Lesson
We have now defined and documented a decision that affects eight different teams. This raises an important question: how should the decision-making process itself be managed? In our next lesson, we will address this by exploring how to identify changes that cross domain boundaries and propose lightweight governance mechanisms for handling them, ensuring that such decisions are made collaboratively and effectively.