Remote Work Tools

Code ownership in a distributed team prevents the “nobody knows who’s responsible for this” problem. When a service breaks at 2am, ownership records tell the on-call engineer exactly who to page. When a PR touches a security-sensitive module, ownership rules auto-request the right reviewer.

This guide covers the tools that enforce, visualize, and maintain code ownership at scale.


GitHub CODEOWNERS (Baseline)

Every GitHub repo supports a CODEOWNERS file that auto-assigns reviewers based on file paths. It’s free, built-in, and a required foundation before adding heavier tooling.

# .github/CODEOWNERS

# Global fallback owner
*                          @your-org/platform-team

# Backend services
/services/auth/            @alice @bob
/services/payments/        @payments-team
/services/notifications/   @backend-team

# Infrastructure
/infra/                    @devops-team
/infra/terraform/          @alice @charlie

# Frontend
/frontend/                 @frontend-team
/frontend/src/components/  @design-system-team

# Shared libraries — require two approvals
/libs/                     @your-org/senior-engineers

# Security-sensitive files
/config/secrets.yml        @security-team
/.github/workflows/        @devops-team @security-team

Enforce that CODEOWNERS reviews are not bypassed via branch protection:

Settings > Branches > Branch protection rules > Require review from Code Owners

Check for ownership gaps:

# List files with no CODEOWNERS match
git ls-files | while read f; do
  owner=$(cat .github/CODEOWNERS | awk -v file="$f" '
    $1 != "#" && file ~ gensub(/\*/, ".*", "g", $1) { owner=$2 }
    END { print owner }
  ')
  [ -z "$owner" ] && echo "NO OWNER: $f"
done

Backstage Software Catalog (Team-Scale)

Backstage adds a service catalog that maps ownership at the component and system level, beyond what CODEOWNERS covers.

Install the catalog plugin:

# In your Backstage app directory
yarn --cwd packages/app add @backstage/plugin-catalog
yarn --cwd packages/backend add @backstage/plugin-catalog-backend

Define a component with ownership metadata:

# catalog-info.yaml (in each service repo)
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payments-service
  description: Handles all payment processing
  annotations:
    github.com/project-slug: your-org/payments-service
    pagerduty.com/service-id: P1234AB
spec:
  type: service
  lifecycle: production
  owner: payments-team
  system: financial-platform
  dependsOn:
    - component:auth-service
    - resource:payments-db

Register the catalog entry in app-config.yaml:

catalog:
  rules:
    - allow: [Component, System, API, Group, User, Resource, Location]
  locations:
    - type: github-discovery
      target: https://github.com/your-org/*/blob/main/catalog-info.yaml

The Backstage UI then shows every service, its owner team, its dependencies, and links to runbooks, PagerDuty schedules, and CI pipelines — all in one place.


Sourcegraph Own (Large Codebases)

For monorepos or organizations with hundreds of repos, Sourcegraph’s own feature gives codebase-wide ownership search.

Enable in sourcegraph.yaml:

experimentalFeatures:
  ownStats: true

Sourcegraph supports both GitHub CODEOWNERS files and its own signal-based ownership inference (based on who last modified code). Query ownership:

# Find all files owned by the payments team
file:contains.owner(@payments-team)

# Find files with no owner
-file:has.owner()

# Find owner of a specific file
repo:^github\.com/your-org/payments-service$ file:^src/charge.go select:file.owners

The ownership API is also scriptable for generating reports:

curl -s \
  -H "Authorization: token $SOURCEGRAPH_TOKEN" \
  "https://sourcegraph.yourcompany.com/.api/graphql" \
  -d '{"query": "{ search(query: \"-file:has.owner()\", version: V3) { results { matchCount } } }"}' \
  | jq '.data.search.results.matchCount'

OpsLevel for Service Maturity

OpsLevel layers ownership into a broader service maturity framework — useful when you want to enforce not just “who owns this” but “does the owner maintain it to a standard.”

Connect your GitHub org:

# OpsLevel CLI
npm install -g @opslevel/cli
opslevel configure --api-token $OPSLEVEL_API_TOKEN

Define service ownership via YAML config:

# opslevel.yml (in each service repo)
version: 1
service:
  name: Payments Service
  product: Financial Platform
  tier: tier_1
  lifecycle: production
  owner: payments-team
  language: Go
  framework: gRPC
  repos:
    - https://github.com/your-org/payments-service
  tags:
    - env:production
    - pci:yes

Push service config:

opslevel services create --from-file opslevel.yml

OpsLevel then scores each service against your rubric (does it have an owner, runbook, on-call schedule, passing checks?). Teams see their score and what’s missing.


Automated Ownership Audits

Run weekly audits to catch drift:

#!/bin/bash
# scripts/ownership-audit.sh
# Reports files with no CODEOWNERS entry

set -euo pipefail

REPO_ROOT=$(git rev-parse --show-toplevel)
CODEOWNERS="$REPO_ROOT/.github/CODEOWNERS"
UNOWNED_FILES=()

while IFS= read -r file; do
  # Skip generated files and vendor dirs
  [[ "$file" == vendor/* ]] && continue
  [[ "$file" == *.lock ]] && continue

  matched=false
  while IFS= read -r line; do
    [[ "$line" =~ ^# ]] && continue
    [[ -z "$line" ]] && continue
    pattern=$(echo "$line" | awk '{print $1}')
    if [[ "$file" == $pattern* ]] || [[ "$file" == *$pattern* ]]; then
      matched=true
      break
    fi
  done < "$CODEOWNERS"

  $matched || UNOWNED_FILES+=("$file")
done < <(git ls-files)

if [ ${#UNOWNED_FILES[@]} -gt 0 ]; then
  echo "=== UNOWNED FILES ==="
  printf '%s\n' "${UNOWNED_FILES[@]}"
  echo ""
  echo "Total unowned: ${#UNOWNED_FILES[@]}"
  exit 1
fi

echo "All files have owners."

Add it to CI:

# .github/workflows/ownership-audit.yml
name: Ownership Audit
on:
  push:
    paths:
      - '.github/CODEOWNERS'
      - '**/*.go'
      - '**/*.ts'
  schedule:
    - cron: '0 9 * * 1'  # Every Monday morning

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Check code ownership
        run: bash scripts/ownership-audit.sh

CODEOWNERS Linting

Validate your CODEOWNERS file doesn’t reference deleted teams or invalid paths:

# Install codeowners validator
go install github.com/mszostok/codeowners-validator@latest

# Run validation
codeowners-validator \
  --repository-path=. \
  --owner-checker-token=$GITHUB_TOKEN \
  --checks="files,syntax,owners,duppatterns"

Common errors it catches:


Tool Comparison

Tool Scope Cost Best For
GitHub CODEOWNERS Per-repo, file-level Free All teams, baseline
Backstage Catalog Cross-repo, service-level Free (self-hosted) 50+ services
Sourcegraph Own Monorepo, file-level search Paid Large eng orgs
OpsLevel Service maturity + ownership Paid Compliance-heavy teams

Start with CODEOWNERS in every repo. Add Backstage when you have more services than people can track in their heads. Layer in OpsLevel or Sourcegraph when audits and compliance become a concern.



Built by theluckystrike — More at zovo.one