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
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
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.
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