DEVBLUEPRINTS

Blog

  • Sobre o blog
  • Arquivo
  • Newsletter
  • RSS

Legal

  • Termos e Privacidade
  • Contato
  • Sobre mim

Inscreva-se na Newsletter

Autorizo o envio de comunicações por e-mail ou qualquer outro meio e concordo com os Termos e Política de Privacidade

 

Blog
  • Sobre o blog
  • Arquivo
  • Newsletter
  • RSS
Legal
  • Termos e Privacidade
  • Contato
  • Sobre mim
© 2026 Todos os direitos reservados — Desenhado e construido comFooter Heartpor Ednaldo Luiz
Blog/System Design

Design Docs: how to decide before the code without creating bureaucracy

Learn when to write a Design Doc, what to include, and how to review architecture, trade-offs, rollout and metrics before expensive decisions turn into code.

software architecture
design docs
system design
technical writing
engineering practices
Design Docs: how to decide before the code without creating bureaucracy
Ednaldo Luiz
Ednaldo Luiz
Level: Intermediate
Level:
Published: 07 de setembro de 2026
Last updated: 07 de setembro de 2026
23 min read
views: - views

Introduction

Monday, 10 a.m.

The team is discussing how to prepare the platform for the new mobile application. The current API was built for the web, returns payloads larger than the app needs, and forces the frontend to make several calls to assemble the home screen.

In twenty minutes, three solutions appear.

One person wants to adapt the existing API. Another proposes creating a . A third suggests putting GraphQL in front of everything.

The meeting ends with a familiar sentence:

Let's start with the BFF and adjust along the way.

Two sprints later, the BFF already knows business rules, the cache has no clear invalidation strategy, Security finds out that tokens are being propagated incorrectly, and nobody remembers why GraphQL was dropped.

The problem was not a lack of code.

The problem was committing to a technical direction before making the proposal readable to the people who needed to evaluate it.

A Design Doc exists to bring that conversation forward: to turn a technical direction into something other people can question, improve or reject before it gains code, contracts and inertia.

It does not need to predict every class, method or implementation detail.

It should not become a fifty-field form either. Its job is to make clear the problem, the possible paths, the proposed direction and the evidence that will show whether it worked.

If you already use ADRs, there is overlap: an ADR can also start as a proposal and be accepted or rejected. The most useful difference is the focus. A Design Doc usually evaluates a whole solution; an ADR concentrates one specific architectural decision and its consequences.

For a smaller decision, an ADR may be enough. When the proposal involves several components, contracts, risks, metrics and rollout stages, the Design Doc organizes the whole and can give rise to several ADRs. Further down they appear side by side, next to RFC and Pull Request.

TL;DR

A Design Doc is a technical proposal written before or at the start of implementation to make a solution reviewable while there is still cheap room to change direction.

Use it when there are plausible alternatives, impact across modules or teams, operational risk, long-lived contracts, or decisions that are expensive to reverse.

A good document presents context, goals, non-goals, a high-level solution, alternatives, trade-offs, cross-cutting concerns and validation criteria. The template guides. The problem decides the size.


What a Design Doc is

A Design Doc is a relatively informal document that describes how a technical problem will be tackled and why that direction makes sense.

The important word is proposal.

While the document is under discussion, the architecture can still change. The author is not merely announcing what they intend to build.

They are exposing assumptions and decisions so that other people find problems before the solution materializes into databases, contracts and infrastructure and goes into use.

After that, users and systems start depending on it, and changing direction already requires migration, coordination and more risk.

One way to see its role is:

why change
how to solve
what we trade
how to prove
Context
→
Proposal
→
Trade‑offs
→
Evidence
why change
Context
→
how to solve
Proposal
→
what we trade
Trade‑offs
→
how to prove
Evidence

The model above is not just a didactic way to summarize the role of the document. One of the best known descriptions of the practice comes from Google, where Design Docs are written before implementation and focus on high-level strategy, on the main decisions and on their trade-offs. The practice also helps to find problems early, gather distributed knowledge, consider cross-cutting concerns and preserve organizational memory.

That does not mean a Design Doc is a complete description of the system.

The document has to keep what the code does not preserve well:

  • the conditions that made the problem relevant;
  • the alternatives that were actually available;
  • the constraints of that moment;
  • the risks accepted;
  • the questions that still had no answer;
  • the results used to validate the proposal.

The code will show the final implementation.

On its own, it will not preserve that the team dropped GraphQL because it needed to launch in eight weeks, that nobody had operational experience with the technology, or that the priority was reducing mobile traffic without creating a new API platform.

A Design Doc is not Big Upfront Design

It is easy to confuse documentation written before the code with .

The difference is in what each one claims.

Big Upfront Design tries to eliminate uncertainty through early detail. A Design Doc tries to make uncertainty visible so the team can decide better.

A healthy document can say:

We do not know yet whether the cache will be necessary.

We will validate payload and latency without cache first. The decision will be reopened if p95 exceeds 350 ms.

That is more useful than inventing a complete cache strategy just to make the document look final.

The Design Doc guides the implementation, but it does not pretend that contact with code, load and production will stop teaching new things.

Writing is also a way of designing

An idea can look perfectly clear while it is still in the head of the person who formed it.

When it goes onto paper, the gaps show up:

  • who owns this data?
  • what happens if a dependency becomes unavailable?
  • how do we roll back?
  • which alternative are we really rejecting?
  • are we moving business rules to the wrong place?
  • which metric proves the problem was solved?

Writing forces the reasoning to stop relying on intuition.

The final result matters, but the process of shaping the document already produces value. It turns an individual conviction into a proposal other people can examine.


When a Design Doc is worth writing

The size of the task is not the best ruler.

A mechanical migration that changes five hundred files can have an obvious direction and low architectural risk. A five-line change in how the system determines the current tenant, on the other hand, can shift an entire security boundary.

The question is not "is this task big?".

The question is:

Is there a relevant decision that would be better discussed before it gains inertia in the code?

Five signals help to answer it.

SignalQuestionExample
AmbiguityAre there two or more genuinely plausible solutions?Adapt the current API, create a BFF or adopt GraphQL
Blast radiusHow many modules, consumers or teams may feel the change?A change to an API used by web, mobile and partners
ReversibilityHow much does it cost to back out after the deploy?Data migration or protocol change
CoordinationWho needs to agree or to provide knowledge?Security, platform, data, SRE and consumer teams
LongevityFor how long will the system carry this decision?Main database, authorization model or public contract

A change tends to justify a Design Doc when several of these signals show up together.

A single signal can also be enough when it is critical, such as:

  • a change in authentication or authorization;
  • a change to a public API;
  • an irreversible data migration;
  • a new central operational dependency;
  • a change in consistency semantics;
  • a change that crosses a trust boundary;
  • a rollout with significant risk to users.

Practical heuristic

If the implementation is predictable, local and easily reversible, a good issue or Pull Request is probably enough.

If reasonable people can choose different paths and each path charges a relevant price, the Design Doc starts to pay for itself.

When not to write one

Design Docs also cost time.

It is not worth writing a full document for:

  • a local refactoring with preserved behavior;
  • a simple endpoint that follows an established pattern;
  • a mechanical dependency update;
  • a fix whose cause and solution are evident;
  • a throwaway experiment whose only goal is finding out whether something works;
  • a task where there is no real decision and no relevant trade-off.

A bad sign is the document that turns into an implementation manual:

  1. Create class MobileCatalogController
  2. Create interface CatalogService
  3. Add method getCatalog()
  4. Write unit test

That can be a task, a checklist or a Pull Request description.

If the document only narrates the future code, without discussing alternatives, risks or choices, it would probably be better to write the code.

Prototyping can be part of it

Not every doubt is settled with text.

Some questions only have an answer through measurement, and that is what the exists for:

  • does the driver really support the expected throughput?
  • does the framework allow controlling the required timeout?
  • does the query use the index?
  • does the SDK work with the existing authentication flow?

In that case, the Design Doc does not compete with the prototype. It asks the question, points to the experiment and uses the result as evidence.

In the document, this shows up at both ends:

The same question, at the entrance and at the exit

Open questions. Should the Recommendations timeout be 100 or 150 ms?

References. Parallel composition spike: at 150 ms, 4% of the responses lose the recommendations block; at 100 ms, 11%.

"We tested it and it worked under these conditions" is a better argument than "we believe it will work".

One rule is still missing, and it is the one that separates a spike from simply starting to build: the experiment has a deadline and its code is disposable. Without both, nobody ever decides that the prototype became the solution. It just never gets deleted, and one day it is already in production. That is the scene that opened this article, under another name.


What a good Design Doc has to answer

There is no universal template.

Even so, good documents tend to answer a similar set of questions.

PartQuestion answeredCommon mistake
Header and overviewWhat is this proposal, who answers for it and what is its status?Making the reader find that out in the middle of the text
Context and scopeWhich problem exists and which limits matter?Starting by selling the solution
Goals and non-goalsWhat has to improve and what will not be solved now?Using vague goals or excluding basic obligations
Proposed designHow will the solution work at a high level?Jumping from a generic diagram to classes and methods
AlternativesWhich other paths were plausible?Creating façade options
Trade-offsWhich price does each option charge?Recording benefits only
Cross-cutting concernsWhat changes in security, operation, data and reliability?Treating production as a later detail
Rollout and validationHow do we launch, measure and decide to continue or go back?Taking approval as proof of success
Open questionsWhat still needs to be discovered or decided?Hiding uncertainty to look finished

These parts do not have to become nine chapters in every document.

A Mini Design Doc can solve several of them in a few paragraphs.

The template should bring important questions to mind. It should not force the author to invent content just to fill fields.

Context comes before the solution

Compare the two possible openings of the same document:

Context that decides for the team

We need to create a BFF to improve the performance of the application.

Context that lets the team decide

The home screen of the application makes five requests, transfers 420 KB on the median load and duplicates on the client composition rules that already exist in the backend. On higher latency mobile networks, this delays the first render and increases the drop-off rate for the journey.

In the first one, the solution arrived before the problem was demonstrated.

Notice what changed: instead of discussing how to implement a BFF, the team can now decide which path solves the problem better.

Maybe the BFF is the best direction.

Maybe the existing API can provide a mobile representation.

Maybe the problem is in the images, and a new backend layer solves almost nothing.

The context does not have to be so neutral that it hides urgency. It just should not use the solution as a premise.

Goals have to become evidence

"Improve performance" is not a usable goal.

It does not say which aspect of performance matters, where we are starting from, or how much it needs to improve.

A mature goal works like a small validation contract.

The example below is fictional:

DD-0042-mobile-home-bff.md
## Goal

Reduce the bytes transferred on the home screen of the application.

**Baseline:** p50 = 420 KB per load over the last 14 days.

**Target:** p50 <= 290 KB after the full rollout.

**Outcome metric:** mobile.home.payload_bytes

**Adoption metric:** at least 95% of mobile sessions served by the BFF.

## Guardrails

- Error rate <= baseline + 0.1 percentage point.
- Backend p95 latency <= baseline + 5%.
- Monthly cost within the approved budget.

## Decision criteria

- Pause the rollout if a guardrail is violated.
- Reassess the design if the target is not reached after 7 days.
DD-0042-mobile-home-bff.md
## Goal

Reduce the bytes transferred on the home screen of the application.

**Baseline:** p50 = 420 KB per load over the last 14 days.

**Target:** p50 <= 290 KB after the full rollout.

**Outcome metric:** mobile.home.payload_bytes

**Adoption metric:** at least 95% of mobile sessions served by the BFF.

## Guardrails

- Error rate <= baseline + 0.1 percentage point.
- Backend p95 latency <= baseline + 5%.
- Monthly cost within the approved budget.

## Decision criteria

- Pause the rollout if a guardrail is violated.
- Reassess the design if the target is not reached after 7 days.

That block separates three questions.

  • Outcome metric: did the expected benefit show up?
  • Adoption metric: was the solution actually adopted?
  • Guardrails: was what could not get worse preserved?

Reducing payload and doubling the error rate is not success.

It is also no good to improve the experience of 10% of the sessions and declare victory over the whole system.

Mature processes, such as the Kubernetes Enhancement Proposal template, bring proposal and production closer together by requiring test plans, a graduation and rollback strategy, metrics and readiness criteria. A product Design Doc does not need to copy the Kubernetes process, but it can learn from the principle: the technical decision does not end when the design looks convincing.

Non-goals do not cancel obligations

A non-goal is something related to the problem that could be in scope but was deliberately left out.

For example:

Non-goal: replace the API used by the web frontend.

That keeps the BFF discussion from turning into a rewrite of the whole platform.

But there is a trap. Minimum security is not an optional feature that can be excluded from the design:

Non-goal that misleads

Security of the BFF. It removes from the design an obligation the solution will have to meet anyway in order to reach production.

Non-goal that limits

Introducing a new authentication mechanism. The BFF will reuse the current mechanism and must preserve the existing controls for authentication, authorization and data protection.

The non-goal limits the space of the change.

It does not remove the requirements the solution needs in order to reach production.

The design shows the direction

The design section starts with the overview.

It should let someone understand:

  • which components are added or change;
  • who calls whom;
  • where the data lives;
  • who owns each responsibility;
  • which contracts cross the boundary;
  • where failures can happen;
  • which part is new and which part already exists.

Only after that come the details needed to evaluate the proposal.

One question resolves most of the doubt about the level of detail:

Does this detail change the evaluation of the solution?

If it does, it belongs in the Design Doc.

If it only describes mechanical work, it probably belongs in the code, in the formal API, in the migration or in the task.

Some examples:

  • Public contract: show the fields and behaviors that affect compatibility.
  • Data model: show ownership, keys, cardinality, retention and the relevant consistency.
  • New algorithm: use pseudocode when the reasoning determines correctness or performance.
  • Distributed flow: show sequence, timeout, retry, idempotency and failures.
  • Ordinary classes and methods: leave them to the code.
  • Full OpenAPI or schema: link the artifact, do not paste hundreds of lines.

Diagrams also need a purpose.

A context diagram can show where the new component fits. A sequence diagram can reveal calls, dependencies and failure points. Neither of them is self-explanatory: after the drawing, describe the flow and highlight what needs review.

Alternatives have to lose honestly

A weak proposal makes the preferred option look excellent and turns the others into caricatures.

A more honest comparison lets each alternative win on some axis.

AlternativeWhere it is strongestMain priceOutcome
Adapt the current APILower operational cost and faster deliveryIncreases coupling between web and mobile needsRejected
Composition in the applicationCreates no new serviceMore calls, duplicated logic and greater dependency on the networkRejected
Mobile BFFSpecific contract and control over compositionNew service, deploy, observability and ownershipRecommended
GraphQL gatewayFlexibility for multiple clientsOperational learning curve and a change larger than the current problemReassess later

GraphQL does not have to be technically inferior to lose.

It can be the most flexible option and still not fit the deadline, the operational capacity or the current size of the problem.

That is a real trade-off.

The chosen option does not win everywhere. It serves the goals better within the available constraints.

Production is part of the design

Some concerns cut across any solution and usually show up too late:

  • authentication and authorization;
  • privacy and data residency;
  • observability;
  • capacity and cost;
  • availability and degradation;
  • compatibility;
  • migration;
  • rollout and rollback;
  • support and operational ownership.

There is no need to write a treatise on each item.

The document has to say how the proposal affects each relevant concern and to point to separate analyses when the topic requires depth of its own.

For example:

Observability. The BFF will propagate trace context to the three dependencies. We will have metrics per dependency, timeout and fallback.

Degradation. A failure in recommendations will not take the screen down. The block will be omitted and the response will still carry catalog and pricing.

Rollback. The application will remain able to use the previous composition during the initial rollout.

That alone already changes the quality of the review quite a lot.


From proposal to production

A Design Doc that ends at Approved is incomplete.

Approval shows that the direction looks reasonable with the information available. It does not prove that the implementation respected the design, nor that the result showed up in production.

A lightweight flow can follow five steps.

  1. Write the first draft. The author organizes context, goals, options and proposal. Co-authors can take part when the knowledge is distributed.

  2. Review with people who know the problem. Before sending it to a large audience, involve people close to the domain. They correct basic assumptions without consuming the attention of the whole organization.

  3. Ask for targeted review. Security reviews the trust boundary. SRE looks at operation and failures. Consumer teams check contracts. Each person needs to know which part requires their attention.

  4. Record the decision and implement. The proposal can be approved, rejected, deferred or sent back for revision. During implementation, the document tracks discoveries that change the design.

  5. Validate and preserve the history. After the rollout, compare metrics and guardrails. When the solution stabilizes, preserve the rationale and connect future changes to new documents or amendments.

Author, reviewer and approver are not the same

Mixing the roles creates two problems: nobody knows who decides, and every comment seems to carry veto power.

A practical split:

  • Author or DRI: drives the document, answers questions and keeps the proposal moving.
  • Contributor: provides data, prototypes, alternatives or specific knowledge.
  • Reviewer: evaluates the proposal and points out risks or improvements.
  • Required reviewer: represents an area whose approval is mandatory, such as Security on a trust boundary change.
  • Approver or decision owner: holds the authority to accept the direction.
  • Informed: needs to know the decision happened, but does not block the process.

Not every Design Doc needs to formalize all of these roles.

The larger the blast radius, the more important it becomes to make authority explicit.

Review follows the impact

Reach of the changeExpected reviewDecision
Local and reversiblePeers on the team itselfTechnical owner of the team
Contract between teamsProducers and consumersThe owners involved
Security, data or operationThe specialists responsibleOwner plus mandatory sign-off
Platform or organizationRepresentatives of the affected groupsCross-cutting forum or owner

The mistake lives at the extremes.

With no review, the document becomes a written justification produced after someone already decided alone.

With central approval for everything, the Design Doc joins a queue and the team learns to work around the process.

The review does not need to gather twenty people.

It needs to include the people who can find risks the author cannot see.

Give feedback a deadline

"Take a look whenever you have some time" is not a review process.

The document has to state:

  • by when feedback is expected;
  • who is required;
  • who is optional;
  • who makes the final decision;
  • what happens if the proposal changes materially.

There is no universal deadline.

A local decision can collect comments in a few days. A change to a public API or to shared infrastructure may require a longer window.

The Rust RFC process shows a formal version of this idea: the proposal gets public discussion, the author revises it in response to the feedback, and the decision only happens after a final comment period of ten calendar days, announced publicly. A product squad does not need to copy that mechanism, but it should copy the principle that comments have an explicit moment, an explicit responsibility and an explicit closing.

Silence is not approval by accident

A team can adopt lazy consensus deliberately.

What does not work is leaving the proposal parked, assuming that nobody complained and calling that alignment.

Define the rule beforehand.

Update while the solution is still being born

During implementation, the plan meets reality.

An API does not support the expected behavior. The database picks another plan. A requirement shows up late. The cost of the solution changes.

If the system has not launched yet and the change alters the design, update the document.

Once the solution reaches production and stabilizes, the role starts to change.

The Design Doc stops being only a live proposal and becomes a historical record as well. Silently rewriting the original rationale can erase the very context the document was supposed to preserve.

A simple policy:

  • Before launch: update the document as the design changes.
  • During the rollout: record deviations, decisions and relevant evidence.
  • After stabilization: preserve the rationale and use amendments or new documents for material changes.
  • If a specific architectural decision needs to survive: record a related ADR as well.

A Mini Design Doc in practice

The example below is fictional.

It does not try to be a universal model. It shows how much context fits into a short document when each section has a clear function.

DD-0042-mobile-home-bff.md
# DD-0042: Create a BFF for the mobile home screen

* Status: Under Review
* Owner: Mobile Platform team
* Authors: Ana Souza, Bruno Lima
* Required reviewers: Catalog, Security, SRE
* Decision deadline: 2026-09-04
* Systems impacted: Mobile App, Catalog, Pricing, Recommendations

## Overview

We propose creating a BFF to compose the data of the home screen of the
application into a single response oriented to mobile needs.

The component will aggregate catalog, pricing and recommendations, without
taking on domain rules that belong to those services.

## Context and scope

The home screen makes five requests and transfers 420 KB on the median
load.

The application replicates composition and fallback logic that already
exists in other clients. On higher latency networks, the number of round
trips delays the first render.

The current API also serves the web frontend. Changing it to optimize
mobile only increases the coupling between different experiences.

## Goals

* Reduce the p50 payload from 420 KB to at most 290 KB.
* Reduce the number of calls made by the application from five to one.
* Preserve the current error rate.
* Keep backend p95 latency within 5% of the baseline.
* Allow partial degradation when Recommendations is unavailable.

## Non-goals

* Replace the API used by the web frontend.
* Introduce a new authentication mechanism.
* Move catalog, pricing or recommendation rules into the BFF.
* Create a corporate GraphQL platform.

## Proposed design

The application will call GET /mobile/v1/home.

The BFF will query Catalog, Pricing and Recommendations in parallel.

Catalog and Pricing are required. If one of them fails, the request
fails.

Recommendations are optional. On timeout or error, the block will be
omitted.

The BFF will have no database of its own. A 30 second cache may be used
only for non personalized content, after we measure the solution
without cache.

Flow:

Mobile App
|
v
Mobile Home BFF
|----> Catalog
|----> Pricing
|----> Recommendations

## Initial contract

GET /mobile/v1/home?storeId={storeId}

Response:

* hero
* categories[]
* featuredProducts[]
* recommendations[]
* generatedAt

The absence of recommendations does not make the whole response an error.

## Alternatives considered

### Adapt the current API

It is the fastest option and does not create another service.

It was rejected because it mixes web and mobile needs in the same
contract and keeps composition distributed across clients.

### Keep composition in the application

It avoids a new backend component.

It was rejected because it keeps five round trips, duplicates fallbacks
and pushes knowledge about three internal services to the client.

### GraphQL gateway

It is the most flexible option for multiple clients.

It was not chosen now because it introduces a larger change in platform,
schema governance and operation. It should be reassessed if other clients
show the same need.

### Mobile BFF

It adds a service and operational responsibility.

It was chosen because it concentrates mobile specific composition without
changing the domain contracts and without starting a broader platform.

## Security and privacy

The BFF will reuse the current authentication mechanism.

Authorization will still be applied by the domain services.
The BFF will not trust filters sent by the application alone.

Tokens will not be written to logs.

## Observability and operation

* Distributed tracing between BFF and dependencies.
* Latency, error, timeout and fallback metrics per dependency.
* Dashboard for payload and mobile adoption.
* Operational owner: Mobile Platform team.
* Runbook for total failure and partial degradation.

## Rollout and rollback

The application will temporarily keep the previous flow behind a
feature flag.

Rollout: 5% -> 25% -> 50% -> 100%.

Moving forward depends on the error and latency guardrails.

If one is violated, traffic returns to the previous flow with no new
deploy.

## Validation

Outcome:

* mobile.home.payload_bytes p50 <= 290 KB.

Adoption:

* at least 95% of mobile sessions using the BFF.

Guardrails:

* error rate <= baseline + 0.1 percentage point;
* backend p95 <= baseline + 5%;
* monthly cost within the approved budget.

## Open questions

* Should the Recommendations timeout be 100 or 150 ms?
* Do we need cache after the initial rollout?
* Does the generatedAt field belong to the response or to telemetry?

## References

* PRD of the mobile application
* Home screen dashboard
* Parallel composition spike
* ADR on service to service authentication
DD-0042-mobile-home-bff.md
# DD-0042: Create a BFF for the mobile home screen

* Status: Under Review
* Owner: Mobile Platform team
* Authors: Ana Souza, Bruno Lima
* Required reviewers: Catalog, Security, SRE
* Decision deadline: 2026-09-04
* Systems impacted: Mobile App, Catalog, Pricing, Recommendations

## Overview

We propose creating a BFF to compose the data of the home screen of the
application into a single response oriented to mobile needs.

The component will aggregate catalog, pricing and recommendations, without
taking on domain rules that belong to those services.

## Context and scope

The home screen makes five requests and transfers 420 KB on the median
load.

The application replicates composition and fallback logic that already
exists in other clients. On higher latency networks, the number of round
trips delays the first render.

The current API also serves the web frontend. Changing it to optimize
mobile only increases the coupling between different experiences.

## Goals

* Reduce the p50 payload from 420 KB to at most 290 KB.
* Reduce the number of calls made by the application from five to one.
* Preserve the current error rate.
* Keep backend p95 latency within 5% of the baseline.
* Allow partial degradation when Recommendations is unavailable.

## Non-goals

* Replace the API used by the web frontend.
* Introduce a new authentication mechanism.
* Move catalog, pricing or recommendation rules into the BFF.
* Create a corporate GraphQL platform.

## Proposed design

The application will call GET /mobile/v1/home.

The BFF will query Catalog, Pricing and Recommendations in parallel.

Catalog and Pricing are required. If one of them fails, the request
fails.

Recommendations are optional. On timeout or error, the block will be
omitted.

The BFF will have no database of its own. A 30 second cache may be used
only for non personalized content, after we measure the solution
without cache.

Flow:

Mobile App
|
v
Mobile Home BFF
|----> Catalog
|----> Pricing
|----> Recommendations

## Initial contract

GET /mobile/v1/home?storeId={storeId}

Response:

* hero
* categories[]
* featuredProducts[]
* recommendations[]
* generatedAt

The absence of recommendations does not make the whole response an error.

## Alternatives considered

### Adapt the current API

It is the fastest option and does not create another service.

It was rejected because it mixes web and mobile needs in the same
contract and keeps composition distributed across clients.

### Keep composition in the application

It avoids a new backend component.

It was rejected because it keeps five round trips, duplicates fallbacks
and pushes knowledge about three internal services to the client.

### GraphQL gateway

It is the most flexible option for multiple clients.

It was not chosen now because it introduces a larger change in platform,
schema governance and operation. It should be reassessed if other clients
show the same need.

### Mobile BFF

It adds a service and operational responsibility.

It was chosen because it concentrates mobile specific composition without
changing the domain contracts and without starting a broader platform.

## Security and privacy

The BFF will reuse the current authentication mechanism.

Authorization will still be applied by the domain services.
The BFF will not trust filters sent by the application alone.

Tokens will not be written to logs.

## Observability and operation

* Distributed tracing between BFF and dependencies.
* Latency, error, timeout and fallback metrics per dependency.
* Dashboard for payload and mobile adoption.
* Operational owner: Mobile Platform team.
* Runbook for total failure and partial degradation.

## Rollout and rollback

The application will temporarily keep the previous flow behind a
feature flag.

Rollout: 5% -> 25% -> 50% -> 100%.

Moving forward depends on the error and latency guardrails.

If one is violated, traffic returns to the previous flow with no new
deploy.

## Validation

Outcome:

* mobile.home.payload_bytes p50 <= 290 KB.

Adoption:

* at least 95% of mobile sessions using the BFF.

Guardrails:

* error rate <= baseline + 0.1 percentage point;
* backend p95 <= baseline + 5%;
* monthly cost within the approved budget.

## Open questions

* Should the Recommendations timeout be 100 or 150 ms?
* Do we need cache after the initial rollout?
* Does the generatedAt field belong to the response or to telemetry?

## References

* PRD of the mobile application
* Home screen dashboard
* Parallel composition spike
* ADR on service to service authentication

Notice what the example does not do.

It does not define class names.

It does not paste the whole OpenAPI.

It does not describe every field of the services it queries.

It also does not try to solve GraphQL for the entire company.

The document preserves the decisions that change the evaluation of the proposal: the boundary of the BFF, required and optional dependencies, ownership, fallback, alternatives, rollout and evidence.

To show the contract without sinking into detail, a short representation could look like this:

GET/mobile/v1/home?storeId=rec-01
200OKapplication/json
{
	"hero": {
		"title": "Deals of the week"
	},
	"categories": [],
	"featuredProducts": [],
	"recommendations": [],
	"generatedAt": "2026-08-30T18:20:00Z"
}
200OKapplication/json
{
	"hero": {
		"title": "Deals of the week"
	},
	"categories": [],
	"featuredProducts": [],
	"recommendations": [],
	"generatedAt": "2026-08-30T18:20:00Z"
}

The rest belongs to the formal contract and to the code.

That is the level of decision detail: enough to review the direction, short enough to stay readable.


Design Doc, ADR, RFC or Pull Request

These names have no universal boundaries.

One company calls this kind of document a Design Doc. Another uses RFC, Engineering Review Doc, Technical Proposal or Tech Spec.

The function matters more than the label.

ArtifactMain questionTypical momentLevel of detail
Issue or PRWhich specific change will we make?Implementation close or underwayCode, tasks and local impact
Design DocHow should we solve this problem, and why?Before or at the start of implementationSolution, alternatives, risks and rollout
ADRWhich architectural decision are we evaluating or preserving, and why?From the proposal to the decision recordOne decision, alternatives and consequences
RFCWhich proposal needs broader comment and alignment?Before standardization or a cross-cutting changeVaries with the organization

A Design Doc can give rise to an ADR.

The broader document keeps the problem, the flows, the experiments and the deployment strategy. The ADR concentrates one specific architectural decision, and it can start as a proposal and remain afterwards as a historical record.

For example:

DD-0042: Mobile Home BFFADR-0018: BFF will not have its own databaseADR-0019: Recommendations will degrade partiallycontains several decisions
Two decisions from DD-0042 become records of their own, findable without opening the whole document.

An RFC can play exactly the role this article assigns to the Design Doc.

There is no problem with that.

The mistake is keeping four nearly identical formats without anyone knowing when to use each one.

A practical rule:

Use the lowest cost artifact that preserves enough context for the decision at hand.

If a good Pull Request description solves it, stop there.

If there is a whole solution to review, use a Design Doc.

If a specific architectural decision needs to be discussed and then preserved, use an ADR.

If the proposal requires broad comment or organizational standardization, your process may call it an RFC.


How to adopt Design Docs without creating a registry office

Adoption does not start by buying a tool or creating an architecture board.

It starts with the next ambiguous decision.

  1. Choose a minimal document. Use overview, context, goals, non-goals, design, alternatives, risks and validation. Remove whatever brings no information.

  2. Define when it applies. Use ambiguity, impact, reversibility, coordination and longevity. Do not use the number of files or the size of the estimate.

  3. Choose one official source. Git, a wiki or a collaborative editor can all work. The problem starts when there are three competing copies.

  4. Define who reviews and who decides. Required reviewer, approver and deadline need to be clear when the impact justifies it.

  5. Connect document and implementation. The Design Doc points to issues and Pull Requests. The implementation points back to the document.

  6. Validate after the launch. Compare target, adoption and guardrails. Record the result instead of abandoning the document in the Approved state.

  7. Adjust the process with use. A field nobody fills in goes away. A review that finds no risk gets redirected. A document nobody ever consults calls for investigation.

Where to store them

For teams that naturally work in Git, a simple structure can be:

  • project/
    • docs/
      • design/
        • README.md
        • DD-0042-mobile-home-bff.md
        • template.md
      • adr/
        • ADR-0018-bff-without-own-database.md
        • ADR-0019-recommendations-partial-degradation.md
    • .github/
      • CODEOWNERS

The README.md can work as an index, showing status, owner, domain and impacted systems.

GitHub lets you use CODEOWNERS to automatically request reviews from the people or teams responsible for certain paths and, when branch protection is configured for it, to require approval from those owners.

That can serve highly technical documents:

.github/CODEOWNERS
/docs/design/security/   @security-team
/docs/design/payments/   @payments-architecture
/docs/design/platform/   @platform-team
.github/CODEOWNERS
/docs/design/security/   @security-team
/docs/design/payments/   @payments-architecture
/docs/design/platform/   @platform-team

But Git is not automatically the best choice for every organization.

If product, legal, design or operations take part regularly, a collaborative editor or wiki can reduce friction.

In larger companies, a hybrid model usually works better:

  • a clearly defined canonical source;
  • writing close to whoever decides;
  • publication in a searchable catalog;
  • links between document, code, ADRs and operation.

The tool should make writing, review, discovery and maintenance easier.

Everything else is preference.

Five ways to kill the practice

  • Turning the template into proof of obedience: the author fills fields with no information just because they exist.
  • Writing after the code: the document becomes a retrospective rationalization of the first solution implemented.
  • Inviting everybody to review everything: attention gets diluted and nobody knows who really has to respond.
  • Defining neither decision nor deadline: the document accumulates comments but never leaves the discussion state.
  • Measuring the number of documents: Design Docs can serve as evidence of influence and technical reasoning, but turning their count into a KPI rewards volume, not quality.

The sign of success is not a full folder.

It is finding problems early, making clearer decisions, reducing repeated discussions and being able to explain why a solution took a certain shape.


Before we close, five situations to judge: when the document pays for itself, what a non-goal can exclude, what counts as success after the rollout and what needs to become an ADR.

Question1/5

Four changes land in the same sprint. Which one most looks like a Design Doc?


Conclusion

The first version of the BFF could have started with code. One endpoint, three HTTP clients, a DTO and the promise of fixing the rest later.

That looks fast until the team finds out, during implementation, that each person was solving a different problem.

The Design Doc puts that conversation in the right place.

It does not eliminate uncertainty. It keeps uncertainty from staying hidden until production.

That is the central point:

A Design Doc does not exist to predict all the code. It exists to make a technical decision reviewable before changing your mind gets expensive.

There is probably an open decision in your team right now, one of those that has already come up in two meetings and keeps receiving different solutions.

Before choosing the technology, write five things:

  1. which observable problem needs to be solved;
  2. which paths are genuinely plausible;
  3. why the preferred option fits the context better;
  4. which price the team accepts paying;
  5. which evidence will make the proposal continue or come back to the table.

If no trade-off shows up, maybe a good Pull Request is enough.

If the decision gets clearer before the first line reaches production, the Design Doc has already paid for itself.

On this page

Share

References

A practical writing guide, plus three open processes you can read from the inside to see how proposals, review windows and ownership work in practice.

Ednaldo Luiz
GitHubLinkedInPortfólio

Ednaldo Luiz

Software Architect and Engineer | Java & AI

Software Engineer focused on architecture and performance. I work with Java/Spring Boot, well-structured SQL, scalable services on AWS, and GenAI solutions with RAG (LangChain + vector databases). I value readable code and well-justified decisions.