How to Build a Knowledge Base for a Dev Team
How to build a knowledge base for a dev team — a practical guide for engineering teams who want a shared knowledge system that engineers actually use
Developer Knowledge
How to write architecture decision records (ADRs) — a practical guide for engineers and tech leads who want to document the significant architectural
Every codebase accumulates decisions. Some are small: a naming convention, a logging format, a default timeout value. Others are structural: the choice of database, the service boundary design, the authentication approach. The structural ones shape every subsequent decision made in the system.
The problem is that architectural decisions, once made, become invisible. The database is PostgreSQL not because someone evaluated every database and documented why — it's PostgreSQL because it was the obvious choice, or because the team knew it, or because of a specific feature requirement that's long since been addressed. A developer joining the team two years later looks at the schema, asks "why are we using this partitioning approach?" and gets an answer that amounts to "historical reasons" or "ask James, he built this, but he left."
This is the problem Architecture Decision Records (ADRs) solve: they create a durable, searchable record of significant architectural decisions — what was decided, what the alternatives were, and why the decision went the way it did. Not every decision needs an ADR. The decisions that do are the ones where future developers will ask "why?" and deserve a better answer than git blame and archaeology.
An ADR is a short document — typically 200-500 words — that records one architectural decision. It answers:
An ADR is not:
The key distinction: ADRs document decisions that have been made, not proposals for what to do. An ADR is written when the decision is settled — often immediately after, sometimes during. It records the outcome of a deliberation, not the deliberation itself.
The ADR format was proposed by Michael Nygard in a 2011 blog post, "Documenting Architecture Decisions," where he described a lightweight format for capturing decisions in a way that would survive personnel turnover, codebase evolution, and the passage of time.
Nygard's observation: most architecture documentation gets written in big batch design documents that become stale immediately after the first decision changes. The alternative is lightweight records — short, focused, co-located with the code they document — that stay current because they're easy to write and easy to update.
The format he proposed has been widely adopted, refined, and extended. The core structure has remained stable because it works.
The standard ADR has five sections:
# ADR-[NNN]: [Short title]
**Status:** [Proposed | Accepted | Deprecated | Superseded by ADR-NNN]
[1-3 paragraphs describing the situation that led to this decision. What problem needed to be solved? What constraints existed? What was the state of the system or requirements at the time?]
[1-2 paragraphs stating what was decided. Use the form: "We will [do X]." Be specific — this is the record of what was chosen.]
[Brief description of options that were evaluated but not chosen, and the key reason each was not selected.]
[What happens as a result of this decision? Include both positive and negative consequences. What does this decision make easier? What does it make harder or foreclose?]
[Optional: links to relevant documents, RFCs, issues, design docs, discussions.]
---
Status: The lifecycle marker. "Proposed" means a decision is being discussed but not finalized. "Accepted" means the decision has been made and is in effect. "Deprecated" means the decision is no longer in force and a new approach has been adopted. "Superseded by ADR-NNN" provides a trail from the old decision to the new one.
Context: The hardest section to write and the most valuable to future readers. The context is what makes the decision legible years later — it explains the world as it appeared when the decision was made. What requirements drove this? What constraints existed (time, team expertise, budget, existing infrastructure)? What had already been decided that this decision had to work with?
Good context is specific: "At the time of this decision, our team had three engineers with deep PostgreSQL experience and no one with production experience in MongoDB. The system was expected to serve 50,000 users in year one, with potential to scale to 500,000 in year three. We had an existing PostgreSQL RDS instance that would be reused."
Poor context is vague: "We needed to choose a database. We considered the team's expertise and scaling requirements."
Decision: Concise and definitive. "We will use PostgreSQL as the primary relational database for all application data." Not "we considered PostgreSQL." Not "PostgreSQL was chosen." Active, specific, and past-committed.
Alternatives Considered: This is what makes the decision useful for future re-evaluation. When the context changes (new team expertise, new scaling requirements, new cost pressures), the alternatives section tells the next engineer what was evaluated and what the key objection was. If the key objection no longer applies, the ADR should be superseded.
You don't need exhaustive alternatives — the ones seriously considered are sufficient. For each alternative, one to three sentences: what it was and why it wasn't chosen.
Consequences: Both sides. Positive: what this decision enables. Negative: what it forecloses or makes harder. A decision record that acknowledges the trade-offs is more trustworthy and more useful than one that presents only the rationale for the choice.
Setup: A team is transitioning their API layer from REST to GraphQL for their mobile clients. They decide to write an ADR.
# ADR-014: Adopt GraphQL for Mobile Client API Layer
**Status:** Accepted
Our mobile clients (iOS and Android, ~180,000 active users) are experiencing significant performance problems due to API over-fetching. The REST endpoints return full resource representations; mobile clients use 30-60% of the fields returned. On a 3G connection, the extra payload adds 200-400ms per request.
The backend team has 4 engineers; 2 have GraphQL experience from prior roles. The existing REST API serves 3 web clients as well as the mobile clients. We cannot break the web client API. The decision must cover mobile-only without disrupting the existing REST layer.
We evaluated this in Q4 2025 during the mobile performance sprint, with an initial prototype built over two weeks.
We will introduce a GraphQL API layer (using Apollo Server) for all new mobile client endpoints. The existing REST API for web clients remains in place and will not be migrated at this time.
The GraphQL layer runs as a separate service and calls the existing internal REST services, acting as a BFF (Backend for Frontend). No business logic is duplicated; all data access goes through existing services.
Sparse fieldsets via REST (JSONAPI-style): Would reduce over-fetching for mobile without introducing a new query language. Rejected because it requires client-side specification of every needed field per request type — significant mobile client engineering effort with no runtime flexibility once fields are specified.
Migrate to a single GraphQL API for all clients: Would eliminate the dual API complexity long-term. Rejected for now because the web client codebases are extensive; migrating them adds 6-8 weeks of frontend work to a mobile performance sprint. Revisit in 2026 Q2 if mobile GraphQL adoption goes well.
gRPC: Better performance than GraphQL for internal service-to-service communication but worse tooling for external mobile clients (no easy browser support, limited ecosystem for iOS/Android). Not suitable as a public client API.
Positive:
Negative:
---
ADRs belong with the code they document. The standard location is a docs/decisions/ directory at the root of the repository (or the service directory in a monorepo). File naming convention: NNN-short-title.md, where NNN is a sequential number (014-adopt-graphql-for-mobile.md).
The sequential number matters: it creates a chronological trail of decisions. ADR-020 that supersedes ADR-007 can link directly to ADR-007; readers can see the evolution of a decision.
In a monorepo: Place ADRs at the service or subsystem level if the decision is service-specific, or at the root level if it affects the whole monorepo. Don't put all ADRs at the root if they're actually scoped to individual services.
Discoverability: Add an ADR-README.md or index entry in your main docs/ directory that lists ADRs with their status and one-line description. Most ADR discovery happens through search (git grep, GitHub search), but an index makes the collection browsable.
Not every decision needs one. The signal for "this needs an ADR":
The decision is reversible, but reversal would be expensive. Database choice, authentication approach, event sourcing vs. CRUD, service boundary design — changing these is possible but costs weeks of work. Record them.
The decision will affect future decisions. Any architectural choice that other decisions depend on — the choice of framework, the data model approach, the deployment target — is load-bearing in a way that justifies documentation.
The context will not be obvious to future engineers. If a developer joining in two years would look at this decision and wonder "why did they do it this way?", the decision needs documentation.
The decision required deliberation. If it was an obvious choice, documentation adds little. If multiple alternatives were seriously evaluated and one was chosen, the evaluation is the documentation.
Do not write an ADR for: naming conventions (put those in a contributing guide), library version pins (captured in package files), implementation details within a service (captured in inline comments or module docs), or decisions that will clearly be revisited within weeks.
adr-tools (github.com/npryce/adr-tools): CLI by Nat Pryce for creating, querying, and linking ADRs from the command line. Commands: adr new [title] creates a new ADR with the next sequential number; adr list lists all ADRs; adr supersede NNN [title] creates a new ADR and marks the old one as superseded. Installs via Homebrew.
Log4brains (github.com/thomvaill/log4brains): Generates a searchable static site from your ADR markdown files. Good for teams that want an ADR knowledge base that's browsable without navigating file trees.
Manual: For small teams, a docs/decisions/ directory with numbered markdown files and a manually maintained index is fully sufficient and has no setup overhead.
docs/decisions/ directory in the repository is the standard.Architecture Decision Records are the lightweight alternative to documentation that never gets written and archaeology that wastes hours. A 300-400 word document written at decision time — context, what was chosen, alternatives not chosen, consequences — answers the question every new engineer eventually asks: "why does the system work this way?" The investment is ten minutes per significant decision. The return is hours of onboarding time saved per new team member, reduced re-litigation of settled decisions, and a legible trail of architectural intent that survives personnel turnover and codebase evolution.
To go deeper, check out AI Knowledge Management in 2025.
More WebSnips articles that pair well with this topic.
How to build a knowledge base for a dev team — a practical guide for engineering teams who want a shared knowledge system that engineers actually use
How to document a microservices architecture — a practical guide for engineering teams navigating service sprawl, where the challenge is not documenting
How to keep a changelog developers trust — a practical guide for engineering teams who want a CHANGELOG.md that consumers of their API or library actually
How to save and organize design docs — a practical guide for engineers and engineering teams who want their design documents to remain findable, useful
How to take notes during code review — a practical guide for engineers who want to get more from code review than the immediate feedback loop: building a
How to track tech-debt decisions — a practical guide for engineering teams who want to manage their technical debt as intentional trade-offs rather than