Architecture Decision Records are short documents that capture important engineering choices. They explain the context, the options you considered, the decision you made, and the consequences. ADRs help future maintainers understand why the system looks the way it does.
The ADR plugin brings those records into Backstage so they sit next to the services they affect. It adds an entity view that lists ADR files from your repo and shows title, status, and date. You can open each ADR as rendered markdown. Relative links and images are rewritten so diagrams and references load as expected. The plugin understands common MADR formats version 2 and version 3. You can tailor file selection and content formatting if your team uses a different template.
Search is built in. Engineers can find ADRs across the catalog and jump straight to the service that owns them. This makes onboarding easier, speeds up reviews, and gives teams a clear history for audits. The plugin works with multiple code hosts through Backstage integrations, and it can serve images from private repos through the backend so content stays accessible.
Installation Instructions
These instructions apply to self-hosted Backstage only. To use this plugin on Roadie, visit the docs.
Step 1: Install the frontend package
# from your Backstage root
yarn --cwd packages/app add @backstage-community/plugin-adr
Step 2: Add the ADR tab to your entity page
// packages/app/src/components/catalog/EntityPage.tsx
import { EntityAdrContent, isAdrAvailable } from '@backstage-community/plugin-adr';
import { EntityLayout } from '@backstage/plugin-catalog';
const serviceEntityPage = (
<EntityLayout>
{/* other tabs */}
<EntityLayout.Route if={isAdrAvailable} path="/adrs" title="ADRs">
<EntityAdrContent />
</EntityLayout.Route>
</EntityLayout>
);
export default serviceEntityPage;
Step 3: Annotate your entities with the ADR location
# catalog-info.yaml
metadata:
annotations:
backstage.io/adr-location: docs/adrs
The value can be a path relative to this catalog file or an absolute URL to the directory that holds the ADR markdown files.
repo-root
README.md
src
catalog-info.yaml
docs
adrs
0001-use-adrs.md
0002-use-cloud.md
Step 4: Configure integrations in app config
Add the source control hosts you read ADRs from. Here is a simple GitHub entry. Adjust for your hosts.
# app-config.yaml
integrations:
github:
- host: github.com
token: ${GITHUB_TOKEN}
Step 5a: Install the backend plugin (legacy backend)
# from your Backstage root
yarn --cwd packages/backend add @backstage-community/plugin-adr-backend
Create the backend router
// packages/backend/src/plugins/adr.ts
import { createRouter } from '@backstage-community/plugin-adr-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
reader: env.reader,
cacheClient: env.cache.getClient(),
logger: env.logger,
});
}
Wire it into the backend
// packages/backend/src/index.ts
import adr from './plugins/adr';
// inside main startup
const adrEnv = useHotMemoize(module, () => createEnv('adr'));
apiRouter.use('/adr', await adr(adrEnv));
These legacy backend steps come from the backend plugin guide.
Step 5b: Install the backend plugin (new backend system)
If your app uses the new backend system do this instead of Step 5.
# from your Backstage root
yarn --cwd packages/backend add @backstage-community/plugin-adr-backend
Register the feature in the backend
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
backend.add(import('@backstage-community/plugin-adr-backend'));
// other features
backend.start();
Step 7: Add ADRs to search results optional
Install the ADR search backend module
# from your Backstage root
yarn --cwd packages/backend add @backstage-community/search-backend-module-adr
Register it in the new backend system along with search
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
backend.add(import('@backstage/plugin-search-backend/alpha'));
backend.add(import('@backstage-community/search-backend-module-adr'));
backend.start();
You can tune the index schedule under the key search.collators.adr in app config if needed. These steps come from the search module guide.
Step 8: Show ADR results on the search page (optional)
// packages/app/src/components/search/SearchPage.tsx
import React from 'react';
import { SearchResult } from '@backstage/plugin-search';
import { List } from '@material-ui/core';
import { AdrSearchResultListItem } from '@backstage-community/plugin-adr';
import { AdrDocument } from '@backstage-community/plugin-adr-common';
export const SearchPage = () => (
<SearchResult>
{({ results }) => (
<List>
{results.map(({ type, document, highlight, rank }) => {
switch (type) {
case 'adr':
return (
<AdrSearchResultListItem
key={document.location}
result={document as AdrDocument}
/>
);
default:
return null;
}
})}
</List>
)}
</SearchResult>
);
Step 9: Custom ADR filename filter (optional)
If you need to skip template files or enforce a naming scheme add a filter in the entity content
// packages/app/src/components/catalog/EntityPage.tsx
import { EntityAdrContent } from '@backstage-community/plugin-adr';
import { AdrFilePathFilterFn } from '@backstage-community/plugin-adr-common';
const myCustomFilterFn: AdrFilePathFilterFn = path => {
if (path === '0000-adr-template.md') return false;
return /^(decided-adrs\/)?\d{4}-.+\.md$/.test(path);
};
<EntityLayout.Route if={isAdrAvailable} path="/adrs" title="ADRs">
<EntityAdrContent filePathFilterFn={myCustomFilterFn} />
</EntityLayout.Route>;
Step 10: Custom content decorators (Optional)
You can modify how ADR markdown is rendered while keeping default behavior
// packages/app/src/components/catalog/EntityPage.tsx
import { EntityAdrContent, AdrReader, AdrContentDecorator } from '@backstage-community/plugin-adr';
const myCustomDecorator: AdrContentDecorator = ({ content, filename }) => {
if (filename?.includes('security')) {
return { content: `<!-- masked -->\n${content}` };
}
return { content };
};
<EntityLayout.Route if={isAdrAvailable} path="/adrs" title="ADRs">
<EntityAdrContent
contentDecorators={[
AdrReader.decorators.createRewriteRelativeLinksDecorator(),
AdrReader.decorators.createRewriteRelativeEmbedsDecorator(),
AdrReader.decorators.createFrontMatterFormatterDecorator(),
myCustomDecorator,
]}
/>
</EntityLayout.Route>;
Changelog
This changelog is produced from commits made to the Architecture Decision Records plugin since a year ago, and based on the code located here. It may not contain information about all commits. Releases and version bumps are intentionally omitted. This changelog is generated by AI.
Breaking changes
- None
Features
- Add filename context to AdrContentDecorator. Decorators can read the ADR file name
#5486 merged 12 days ago - Add backend extension point to register a custom ADR parser. Support formats beyond MADR such as Nygard
#3475 merged 6 months ago - Hide frontend entity content when the ADR annotation is missing. Use the new frontend system filter
#2020 merged 10 months ago
Bug fixes
- Return other ADRs even if one file fails to parse. Log the bad file
#2612 merged 8 months ago - Fix backend plugin API import
#2418 merged 9 months ago
Maintenance
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.