Tech Radar Governance

Production engineering guide for tech radar governance covering patterns, implementation strategies, and operational best practices.

Tech radar governance is the structural practice of maintaining an organization's technology adoption radar—the curated, time-stamped map of what tools, languages, frameworks, and platforms the organization uses, evaluates, or deliberately avoids. It matters when multiple teams make independent technology choices that need alignment, when technical debt is managed through deliberate retirement rather than neglect, or when onboarding requires knowing what's already vetted. Governance isn't about control for its own sake; it's about preventing the proliferation of unvetted technologies that create integration burden, security gaps, and operational silence. The radar typically lives as a version-controlled artifact—most commonly markdown files in a git repository, sometimes backed by a database-driven web UI—and governance consists of the processes, commands, and decisions that keep that artifact accurate and actionable.

Add a new technology entry

When a candidate technology enters the evaluation pipeline, the first governance step is creating a radar entry. Most organizations store entries as markdown files in a dedicated tech-radar/entries/ directory, each with a standardized front-matter block and an assessment paragraph. A typical entry format looks like this:

---
key: "react-19"
title: "React 19"
status: "assess"
context: |
  The new concurrent features and server components story
  requires evaluation before team adoption.
tags:
  - frontend
  - ui
last_reviewed: "2024-06-15"
---

*Assessment goes here.*

To add it, you create the file, git add tech-radar/entries/react-19.md, then git commit -m "Add React 19 to radar - assess". The entry does not become "adopt" automatically—someone with radar ownership merges it, tags the status, and schedules the first review cycle. If your organization uses a web UI rather than raw markdown, the same data model applies: a POST to the radar endpoint with {key, title, status, context, tags} and the server persists the markdown. I've seen curl -X POST https://radar.example.com/entries -H "Authorization: Bearer $TOKEN" -d @react-19.md work against Thoughtworks' Radar JSON export format, but if you're on a custom platform, the endpoint usually expects JSON with at minimum a key, title, and status field drawn from a closed enum—adopt, trial, hold, assess, drop. Omitting context is a common mistake; the radar loses evaluative value if every entry is just a title and a status.

A subtle but frequent sharp edge: the key field must be globally unique within the radar repository. I've seen teams accidentally create two entries with the same key but different titles—usually by forgetting to delete the old entry when re-evaluating a technology—and the merge tool silently overwrites the first with the second, losing the original assessment context forever. Always git log --all -- tech-radar/entries/ | grep "$KEY" before creating a new entry to confirm the key is unused.

Update an entry's status

Once an entry exists, governance requires updating its status as evaluation progresses. The mechanics mirror adding: edit the status field in the markdown front matter, commit, merge. But the sharp edge here is that status changes often don't propagate automatically. I've seen teams where the radar HTML page still showed "trial" three months after the git history recorded "adopt" because the static site generator was cached behind a CDN invalidation step that nobody remembered to run. If you're using a hosted radar service, the UI may have a "Re-render" button, but the underlying API call is often just POST /entries/{key} with the new status payload.

A confusing pattern: hold and assess can look identical in rendered diffs if your template only renders the status badge, not the context paragraph. Always diff the full file, not just the rendered output. Another silent failure: the last_reviewed timestamp may not update when status changes, especially if the reviewer uses a web UI that writes only the status field. If your governance model tracks review cadence by timestamp, this desync means the radar reports an entry as recently reviewed when it hasn't been touched in cycles.

Schedule and run a review cycle

Reviews are the heartbeat of radar governance. Most teams run them quarterly, aligned with planning cycles. The process typically looks like: a review issue is opened in your issue tracker, reviewers are assigned, each reviewer examines every entry in their domain, they update status and context, and the changes are merged. If you're using git, the review cycle might be a dedicated branch radar/review/2024-Q3 that gets rebased onto main after all changes land.

A concrete command pattern I've used to find stale entries: git log --since="3 months ago" -- tech-radar/entries/ | grep -E '^commit' to locate files untouched in a review period, then git diff main..radar/review/2024-Q3 -- tech-radar/entries/ | grep '^[-+].*status' to see what changed. But the silent failure mode is reviewers who approve without reading the assessment, just clicking "Looks good" on the PR. The entry gets merged with a status update but no real evaluative change, and the radar drifts.

If you're automating stale-entry detection via a database backend, a common psql query pattern is:

SELECT key, title, status, last_reviewed
FROM entries
WHERE last_reviewed < NOW() - INTERVAL '90 days'
ORDER BY status ASC;

But last_reviewed is often not updated atomically with the status field. I've seen races where a status change and a last_reviewed update happen in separate transactions, so a review cycle can produce entries with status: "adopt" and last_reviewed: "2023-01-01"—the review happened, the status changed, but the timestamp wasn't updated because the reviewer used the web UI which writes only the status field. Always check both fields, or add a database trigger that auto-sets last_reviewed = NOW() on any entries update.

Promote from trial to adopt

Promoting an entry from trial to adopt is the moment many governance processes obsess over. The typical pattern: an entry starts at assess or trial, a team uses it in a non-critical project, the review cycle confirms no catastrophic failures, and the status moves to adopt. The sharp edge that catches people off guard is that adopt often triggers implicit obligations—compliance reviews, license audits, inclusion in the official supported-software list. I've seen a team move an entry to adopt only to discover the security team hadn't been cc'd on the decision, causing a two-week delay while they performed a vulnerability assessment.

If your governance model has automated guardrails, the promotion might be gated by a policy check. Some orgs require verification that the technology can be safely deployed in your environment—kubectl auth can-i deploy for Kubernetes resources, or a psql check that the extension version has no CVE entries in the National Vulnerability Database. If you're writing a script that automates promotion, the pattern is usually:

curl -X PATCH https://radar.example.com/entries/react-19 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status":"adopt"}'

But the server may reject the PATCH if a linked decision record doesn't exist or if the entry has been held for less than 60 days. The error text you'll typically get is 422 Unprocessable Entity: {"error":"promotion_guard:minimum_hold_period","minimum_days":60}. Know what guard your platform enforces before you try. Another gotcha: the adopt status may automatically flag the entry for inclusion in the organization's supported-software inventory, which then triggers provisioning pipelines. If those pipelines aren't paused during evaluation, you could provision resources for a technology that later gets demoted back to hold, leaving orphaned infrastructure.

Publish or export the radar

The rendered radar—whether a static HTML page, a PDF circulated to executives, or a dashboard embedded in your internal developer portal—is the deliverable. If entries are markdown in git, the publish step is often a CI pipeline: npm run generate-radar or hugo --dir tech-radar that reads the markdown files and outputs a ranked list. I've seen the sharp edge where the generation script sorts by status alphabetically rather than by the intended adoption order, so assess appears before hold because 'a' comes before 'h'. The fix is usually a priority or order field in the front matter that the template respects.

If you're exporting to CSV for stakeholder review, the typical pattern is a script that queries the backend:

curl https://radar.example.com/api/v1/entries?status=adopt | jq '.[] | {key, title, status, tags}' > adopt.csv

But the CSV will miss the context field unless you explicitly request it, and the tags array gets flattened into a pipe-separated string that's harder to filter later. Know what your export includes before you commit to a stakeholder format. Another silent failure: the export script may paginate results, and if you don't loop through all pages, the CSV will only contain the first N entries, creating the illusion that only a subset of technologies have been adopted.

Handle a governance failure

The most common failure mode is entropy: entries accumulate, reviews skip cycles, and the radar becomes a document nobody trusts. When that breaks down, the typical response is a "radar reset"—a governed period where every entry is either validated, updated, or deleted. I've been through a reset where the command was simply git rm tech-radar/entries/*.md followed by a bulk import from the current decision records repository. The error text from an inattentive script was error: pathspec 'tech-radar/entries/deprecated-angularjs.md' did not match any files because some entries had already been moved to an archive directory. The reset teaches you to check git ls-files tech-radar/entries/ before the bulk delete.

Another failure: the radar and the actual production environment diverge. A service might be running React 19 in production even though the radar says hold, because the entry was never updated after the team's successful trial. The sharp edge here is that the radar can actively work against you—teams bypass it because they know it's stale, and then you have no visibility into what's actually running. The cure is often a reconciliation script that compares the radar entries against whatever service registry or configuration management database you use. If you have Kubernetes, a kubectl get all -A -o json | jq '.items[] | select(.metadata.labels."tech-radar-key"' command can surface which technologies are in use without a radar entry, and the diff is usually eye-opening. That diff is the data you need to rebuild the radar from reality rather than from memory.

This page was rewritten on 17 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.