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.
Install the frontend plugin
# from your Backstage root
yarn --cwd packages/app add @backstage-community/plugin-adrAdd the ADR tab to Entity pages
// packages/app/src/components/catalog/EntityPage.tsx
import React from 'react';
import { EntityLayout } from '@backstage/plugin-catalog';
import { EntityAdrContent, isAdrAvailable } from '@backstage-community/plugin-adr';
// add to each entity page layout you use
export const serviceEntityPage = (
  <EntityLayout>
    {/* other tabs */}
    <EntityLayout.Route if={isAdrAvailable} path="/adrs" title="ADRs">
      <EntityAdrContent />
    </EntityLayout.Route>
  </EntityLayout>
);Add ADR results to Search
// packages/app/src/components/search/SearchPage.tsx
import React from 'react';
import { List } from '@material-ui/core';
import { SearchResult } from '@backstage/plugin-search-react';
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}
                  highlight={highlight}
                  rank={rank}
                />
              );
            default:
              return null;
          }
        })}
      </List>
    )}
  </SearchResult>
);Install the backend plugin for the old backend system
# from your Backstage root
yarn --cwd packages/backend add @backstage-community/plugin-adr-backendCreate the plugin 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 the router into the backend
// packages/backend/src/index.ts
import adr from './plugins/adr';
// inside main
const adrEnv = useHotMemoize(module, () => createEnv('adr'));
apiRouter.use('/adr', await adr(adrEnv));Install the backend plugin for the new backend system
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
// add the ADR backend feature
backend.add(import('@backstage-community/plugin-adr-backend'));
// other features
backend.start();Configure integrations
Add your source control hosts under integrations in app config. Here is a GitHub example.
# app-config.yaml
integrations:
  github:
    - host: github.com
      token: ${GITHUB_TOKEN}Annotate entities with the ADR location
Add the annotation to each entity that has ADRs.
# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: my-service
  annotations:
    backstage.io/adr-location: docs/adrs
spec:
  type: service
  owner: team-a
  lifecycle: productionThe path is relative to the catalog file. You can use an absolute URL if you store ADRs outside the repo.
Example layout
repo-root
  catalog-info.yaml
  docs
    adrs
      0001-use-adrs.md
      0002-use-cloud.mdAdd ADRs to Search with the new backend system
Install the ADR search backend module.
# from your Backstage root
yarn --cwd packages/backend add @backstage-community/search-backend-module-adrRegister search backend and the ADR collator.
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
// search backend
backend.add(import('@backstage/plugin-search-backend/alpha'));
// ADR collator module
backend.add(import('@backstage-community/search-backend-module-adr'));
backend.start();Optional config for the collator
# app-config.yaml
search:
  collators:
    adr:
      schedule:
        frequency: { minutes: 30 }
        timeout: { minutes: 10 }Optional frontend tweaks
You can filter which files show up. You can also decorate the rendered content.
// packages/app/src/components/catalog/EntityPage.tsx
import React from 'react';
import { EntityLayout } from '@backstage/plugin-catalog';
import {
  EntityAdrContent,
} from '@backstage-community/plugin-adr';
import { AdrFilePathFilterFn } from '@backstage-community/plugin-adr-common';
const myFilter: AdrFilePathFilterFn = path => {
  if (path === '0000-adr-template.md') return false;
  return /^(decided-adrs\/)?\d{4}-.+\.md$/.test(path);
};
export const serviceEntityPage = (
  <EntityLayout>
    <EntityLayout.Route path="/adrs" title="ADRs">
      <EntityAdrContent filePathFilterFn={myFilter} />
    </EntityLayout.Route>
  </EntityLayout>
);Use content decorators if you need to transform markdown before rendering.
// packages/app/src/components/catalog/EntityPage.tsx
import React from 'react';
import { EntityLayout } from '@backstage/plugin-catalog';
import {
  EntityAdrContent,
  AdrReader,
} from '@backstage-community/plugin-adr';
const myDecorator = ({ content, filename }: { content: string; filename?: string }) => {
  if (filename?.includes('security')) {
    return { content: `> Security review required\n\n${content}` };
  }
  return { content };
};
export const serviceEntityPage = (
  <EntityLayout>
    <EntityLayout.Route path="/adrs" title="ADRs">
      <EntityAdrContent
        contentDecorators={[
          AdrReader.decorators.createRewriteRelativeLinksDecorator(),
          AdrReader.decorators.createRewriteRelativeEmbedsDecorator(),
          AdrReader.decorators.createFrontMatterFormatterDecorator(),
          myDecorator,
        ]}
      />
    </EntityLayout.Route>
  </EntityLayout>
);Changelog
This changelog is produced from commits made to the Architecture Decision Records plugin since a year ago. 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.
