Δ Delta
Read Online · Part II — Core Patterns · Chapter 7 of 19

Documentation That Gets Read

From the free book Delta: Closing the Specification Gap (Dhuri, 2026). The PDF is the canonical edition — figures, tables, and code formatting are simplified online.

Write for the developer at 3am, not the developer who wrote it

“Most documentation is written for the writer. It explains what the code does. The developer who reads documentation knows what it does — they want to know when to call it.” — The single constraint that transforms documentation quality: ‘The most important sentence is WHEN to call this, not what it does.’ In This Chapter

  1. The documentation quality formula
  2. Structured logging for Serilog (.NET 9) and SLF4J (Java 21)
  3. Architecture Decision Records from meeting notes
  4. Audience-tuned documentation from a single source
  5. Operational runbooks that work at 3am

PATTERN — The Log That Could Not Be Searched

PATTERN-3946

A FinTech platform’s SRE team was investigating a payment processing delay. They needed all events for PaymentId abc-123. Kibana search: zero results. The payment was definitely in the database. The log statement that recorded it: _logger.LogInformation( $”Payment {payment.Id} processed in {stopwatch.ElapsedMilliseconds}ms”). The problem: string interpolation in Serilog embeds the PaymentId inside a plain message string, not as a searchable structured field. Their Kibana dashboard had no queryable PaymentId field — it had only opaque message strings.

BEFORE — Tier 1 (What most developers type) "Write documentation for this PaymentService.ProcessAsync method." AFTER — Tier 3 (Specification Frame) "The most important question your documentation answers is: WHEN ,→ should a caller invoke this method? Not what it does — WHEN. ,→ Include: the specific use case (not a capability description), ,→ the state change produced, each ’parameters domain meaning (not ,→

just its type), each Result<T> variant and when it occurs,
,→ thread safety, and any side effects the caller might not expect ,→ ." Result: Tier 1: 'Processes a payment. Parameters: command ( ,→ ProcessPaymentCommand). Returns: Result<PaymentConfirmation>.' ,→ Tier 3: 'Call this when a customer completes checkout and their ,→

card token is available. Do NOT call this for quote generation

,→

— use GetQuoteAsync for that. Returns GATEWAY_DECLINED if the

,→ issuing bank …refuses' C# — Serilog: Structured Parameters vs String Interpolation // CORRECT: structured parameters — PaymentId is a queryable field in ,→

Kibana/Datadog

_logger.LogInformation( "Payment {PaymentId} processed in {ElapsedMs}ms for {Amount:F2}", payment.Id, stopwatch.ElapsedMilliseconds, command.Amount.Amount); ,→ _logger.LogWarning( "Gateway declined: paymentId={PaymentId} ref={Ref} reason={Reason ,→ }", payment.Id, command.PaymentReference, result.DeclineReason); // WRONG: string interpolation — stores one opaque string, not ,→ queryable fields // Kibana/Datadog CANNOT filter by PaymentId using this approach _logger.LogInformation( $"Payment {payment.Id} processed in {stopwatch. ,→ ElapsedMilliseconds}ms"); Java 21 — SLF4J: Placeholders vs String Concatenation // CORRECT: {} placeholders — evaluated lazily, structured in modern ,→ appenders log.info("Payment {} processed in {}ms amount={}", payment.getId(), elapsed, command.amount().forOutput()); log.warn("Gateway declined: paymentId={} ref={} reason={}", payment.getId(), command.paymentReference(), result. ,→ getDeclineReason()); // WRONG: String concatenation — evaluated eagerly even at disabled ,→ log levels // AND loses structured field separation in log aggregation platforms log.info("Payment " + payment.getId() + " processed"); // also wrong log.info(String.format("Payment %s processed", payment.getId())); // ,→ also wrong

Key takeaways

most important question is WHEN to call this.’ Not what it does. When. + Structured logging (Serilog parameters, SLF4J placeholders) is the most commonly missed standard in AI-generated code. Include it in every generation prompt as a constraint. + ADRs must include: all considered options (including rejected), honest bad consequences, and CLARIFY markers for gaps. An ADR without bad consequences is dishonest. + AI documentation may be the highest-leverage, lowest-risk return in this book. The blank-page problem disappears entirely.

TRY IT NOW

  1. Pick three methods lacking complete documentation. Apply the ’WHEN to call this’ recipe. Time it. Compare quality to manually written docs.
  2. Search your codebase for ’LogInformation($’ or ’log.info(String. format’. Count instances. Prioritize the top 3 services for structured logging migration.

3. Name an architectural decision made in the last 6 months without an ADR. Write one from memory using Recipe 7.3.

UP NEXT — Chapter 8: Architecture Decisions Without Blind Spots

Documentation records decisions after they are made. Chapter 8 puts the model to work while they are being made: structured tradeoff analysis, surfaced failure modes, and the questions an architecture review should have asked.

66

Cite this chapter
Dhuri, Sandeep. (2026). Delta: Closing the Specification Gap. Acuity Press. Chapter 7.
DOI