Open source analytics for developer health and team operating modes.
Dev Health Ops ingests engineering activity from Git providers, work trackers,
deployments, incidents, and local repositories; normalizes it into persisted
evidence; computes inspectable metrics; and serves those metrics to the
GraphQL/API layer used by dev-health-web.
Developer health tooling often drifts into expensive, opaque scorecards that are easy to misuse. This project is intentionally different:
- Accessibility over extraction: derive insight from data teams already own.
- Learning, not judgment: show operating signals, not individual rankings.
- Trends over absolutes: emphasize change over time and distributions.
- Inspectable by default: metrics trace back to schemas, queries, and evidence.
Non-goals:
- Individual leaderboards or performance scores
- HR/performance-management workflows
- Dashboards that hide definitions, provenance, or missing data
Dev Health Ops follows a strict pipeline boundary:
Providers → Processors → Sinks → Metrics → API / Visualization
- Providers fetch raw provider data from GitHub, GitLab, Jira, Linear, local Git, CI/CD, deployments, incidents, and synthetic/demo sources.
- Processors normalize provider records into internal models.
- Sinks persist computed outputs. Analytics persistence is ClickHouse-only.
- Metrics jobs compute daily rollups, DORA, complexity, risk, investment, AI workflow, and work graph outputs from persisted data.
- API/GraphQL serves persisted analytics to
dev-health-weband other consumers.
Python remains the owner of the API, GraphQL schema, provider fetch and
normalization, processors, and current Celery job implementations. The
repository also contains an additive Go worker-runtime foundation under cmd/
and internal/; adding those process shells does not move a job out of Python
or change its routing.
The primary visualization surface is now dev-health-web. Grafana is optional,
and this repository no longer ships the old sample dashboard gallery in this
README.
Use the package directly:
pip install dev-health-ops
dev-hops --helpFor local development from this repository:
pip install -r requirements.txtThe installed command is dev-hops.
Dev Health Ops uses two databases with different responsibilities:
| Layer | Backend | Environment variable | Purpose |
|---|---|---|---|
| Semantic | PostgreSQL | POSTGRES_URI |
Users, organizations, settings, credentials |
| Analytics | ClickHouse | CLICKHOUSE_URI |
Commits, PRs/MRs, work items, metrics, graph data |
ClickHouse is required for analytics features. MongoDB, SQLite, and PostgreSQL analytics sinks have been removed or deprecated; SQLite remains only for narrow test/local fixture paths.
Start local services and run migrations:
docker compose up -d postgres clickhouse valkey
export POSTGRES_URI="postgresql+asyncpg://postgres:postgres@localhost:5555/postgres"
export CLICKHOUSE_URI="clickhouse://ch:ch@localhost:8123/default"
dev-hops migrate postgres
dev-hops migrate clickhouseSee docs/contribute/architecture/data-and-storage.md
and docs/reference/cli/index.md for details.
# Local git repository
CLICKHOUSE_URI="clickhouse://ch:ch@localhost:8123/default" \
dev-hops sync git --provider local --repo-path /path/to/repo
# GitHub repository
CLICKHOUSE_URI="clickhouse://ch:ch@localhost:8123/default" \
dev-hops sync git --provider github \
--auth "$GITHUB_TOKEN" \
--owner <owner> \
--repo <repo>
# Pull requests
CLICKHOUSE_URI="clickhouse://ch:ch@localhost:8123/default" \
dev-hops sync prs --provider github \
--auth "$GITHUB_TOKEN" \
--owner <owner> \
--repo <repo>
# Work items from Jira, GitHub, GitLab, Linear, synthetic data, or all providers
CLICKHOUSE_URI="clickhouse://ch:ch@localhost:8123/default" \
dev-hops sync work-items --provider all --backfill 30
# Teams into the ClickHouse team catalog (ClickHouse is the system of record)
CLICKHOUSE_URI="clickhouse://ch:ch@localhost:8123/default" \
dev-hops sync teams --provider config --path src/dev_health_ops/config/team_mapping.yaml --allow-emptyThe bundled team_mapping.yaml is an empty onboarding sample. sync teams
exits non-zero when no teams are persisted; pass --allow-empty only for
intentional empty/no-op syncs such as validating the sample config.
Provider authentication can come from CLI flags or environment variables such as
GITHUB_TOKEN, GITLAB_TOKEN, JIRA_*, ATLASSIAN_*, and LINEAR_API_KEY.
# Daily analytics rollups
CLICKHOUSE_URI="clickhouse://ch:ch@localhost:8123/default" \
dev-hops metrics daily --backfill 30
# Complexity and hotspot snapshots for a repository
CLICKHOUSE_URI="clickhouse://ch:ch@localhost:8123/default" \
dev-hops metrics complexity --repo-path /path/to/repo --backfill 30dev-hops fixtures generate \
--sink "clickhouse://ch:ch@localhost:8123/default" \
--days 30 \
--with-metrics \
--with-work-graphPOSTGRES_URI="postgresql+asyncpg://postgres:postgres@localhost:5555/postgres" \
CLICKHOUSE_URI="clickhouse://ch:ch@localhost:8123/default" \
dev-hops api --reloadOpenAPI docs are available at http://localhost:8000/docs when the API is running. GraphQL is served by the API for the web app.
All production background jobs currently use Celery with Valkey/Redis:
POSTGRES_URI="postgresql+asyncpg://postgres:postgres@localhost:5555/postgres" \
CLICKHOUSE_URI="clickhouse://ch:ch@localhost:8123/default" \
CELERY_BROKER_URL="redis://localhost:6379/0" \
CELERY_RESULT_BACKEND="redis://localhost:6379/0" \
dev-hops workers start-worker --queues default metrics sync reportsPhase 1 of the Go worker migration plan adds the Go runtime, River migration, dual-pool, job-contract, middleware, and operator foundations for future worker, scheduler, reconciler, and stream-runner processes. Every Go deployment profile remains disabled and all registered jobs still route to Celery. A binary building—or even reporting its storage dependencies healthy—does not make a job migrated or canary-ready. Keep the required Celery workers and Beat schedules running until the issue for that job explicitly changes its route and passes the documented parity gates.
Go River processes use POSTGRES_URI for domain state and the separate,
least-privilege WORKER_DATABASE_URI for direct queue control. The latter is a
Go runtime setting, not a replacement Python database alias. River schema is
applied only by the one-shot migration path when MIGRATION_DATABASE_URI and
the two runtime role names are supplied. See Workers for
the coexistence boundary.
Canonical local test commands:
make test:unit
make test:integration
make test:e2e
make test:live-e2e
make test:ciAll tiers route through one entrypoint:
./ci/run_tests.sh <unit|integration|e2e|live-e2e|ci>Notes:
integrationis token-aware and skips provider tests cleanly when credentials are unavailable.live-e2estarts a live backend harness, generates deterministic ClickHouse fixtures, waits for API readiness, and asserts/health,/api/v1/meta, and/api/v1/home.ciblocks onflake8and coverage-gated unit tests.black,isort, andmypyare advisory by default; setSTRICT_QUALITY_GATES=1to make them blocking.- JUnit XML paths are stable under
test-results/junit/and can be overridden withTEST_RESULTS_DIR/JUNIT_XML_*variables.
The repository builds two reusable images from docker/Dockerfile:
| Image | Purpose |
|---|---|
dev-hops-api |
Runs dev-hops api on port 8000 |
dev-hops-runner |
Uses dev-hops as the entrypoint for sync, fixtures, metrics, and maintenance jobs |
Build both images:
IMAGE_REGISTRY=ghcr.io/myorg/dev-health-ops \
VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo latest) \
./scripts/build-images.shRun the API image:
docker run --rm -p 8000:8000 \
-e POSTGRES_URI="postgresql+asyncpg://postgres:postgres@postgres:5432/postgres" \
-e CLICKHOUSE_URI="clickhouse://ch:ch@clickhouse:8123/default" \
dev-hops-api:latestRun a CLI job through the runner image:
docker run --rm -it \
--network dev-health_default \
-v "$(pwd)":/app \
-w /app \
-e CLICKHOUSE_URI="clickhouse://ch:ch@clickhouse:8123/default" \
dev-hops-runner:latest \
metrics daily --backfill 14docs/get-started/index.md: setup and demo datadocs/reference/cli/index.md: full CLI referencedocs/contribute/architecture/data-and-storage.md: PostgreSQL/ClickHouse split and provider → processor → sink boundariesdocs/use/investment/index.md: canonical Investment Viewdocs/use/reports/index.md: Report Center and scheduled reports
- WorkUnits are evidence containers, not categories.
- Investment categorization runs at compute time and persists distributions.
- Theme rollups are deterministic from canonical subcategories.
- UX-time LLM usage is explanation-only and must not recompute categories.
- Analytics persistence goes through ClickHouse sinks, not file exports or debug dumps.