Skip to content

Developers analyzer reference

The developers analyzer computes per-developer contribution statistics across Git history: commit counts, lines added / removed / changed with a per-language breakdown, bus factor risk, and activity time series.

For the conceptual model — what each statistic means and how the bus factor is derived — see Understanding developer analysis. To run it, see the Quick start.


Options

The analyzer reads its options from the codefang run flags. The shared history flags (--head, --limit, --since, --first-parent, --tick-size, --max-changes-per-commit, --languages, --people-dict, --exact-signatures, --anonymize) apply as documented in the CLI reference.

Flag Type Default Description
--empty-commits bool false Count commits whose filtered diff is empty (trivial merges, --allow-empty commits, commits touching only excluded paths) as commits with zero lines.
--anonymize bool false Replace developer names with pseudonyms (Developer-A, Developer-B, …).

Configuration file

The history.devs.* keys of .codefang.yaml describe the same options in the configuration schema, but codefang run applies the flags above only.


Example output

From a twelve-commit fixture with three developers (bins/codefang/tests/devs_e2e.rs); the activity and churn lists are cut to their first tick.

{
  "aggregate": {
    "total_commits": 10,
    "total_lines_added": 30,
    "total_lines_removed": 15,
    "total_developers": 3,
    "active_developers": 1,
    "estimated_total_developers": 3,
    "estimated_active_developers": 1,
    "analysis_period_ticks": 310,
    "project_bus_factor": 2,
    "total_languages": 2
  },
  "developers": [
    {
      "id": 0,
      "name": "alice",
      "email": "alice@example.com",
      "commits": 6,
      "lines_added": 16,
      "lines_removed": 0,
      "lines_changed": 0,
      "net_lines": 16,
      "languages": [
        {"language": "Go", "added": 11, "removed": 0, "changed": 0},
        {"language": "Python", "added": 5, "removed": 0, "changed": 0}
      ],
      "first_tick": 0,
      "last_tick": 310,
      "active_ticks": 6
    },
    {
      "id": 2,
      "name": "carol",
      "email": "carol@example.com",
      "commits": 1,
      "lines_added": 6,
      "lines_removed": 12,
      "lines_changed": 0,
      "net_lines": -6,
      "languages": [
        {"language": "Go", "added": 6, "removed": 12, "changed": 0}
      ],
      "first_tick": 155,
      "last_tick": 155,
      "active_ticks": 1
    }
  ],
  "languages": [
    {
      "name": "Go",
      "total_lines": 22,
      "total_contribution": 37,
      "contributors": {"0": 11, "1": 8, "2": 18}
    }
  ],
  "busfactor": [
    {
      "language": "Go",
      "bus_factor": 2,
      "total_contributors": 3,
      "primary_dev_id": 2,
      "primary_dev_name": "carol",
      "primary_dev_email": "carol@example.com",
      "primary_percentage": 48.64864864864865,
      "secondary_dev_id": 0,
      "secondary_dev_name": "alice",
      "secondary_dev_email": "alice@example.com",
      "secondary_percentage": 29.72972972972973,
      "risk_level": "LOW"
    }
  ],
  "activity": [
    {
      "tick": 0,
      "start_time": "2021-01-01T12:00:00Z",
      "end_time": "2021-01-01T12:00:00Z",
      "by_developer": [{"dev_id": 0, "commits": 1}],
      "total_commits": 1
    }
  ],
  "churn": [
    {
      "tick": 0,
      "start_time": "2021-01-01T12:00:00Z",
      "end_time": "2021-01-01T12:00:00Z",
      "lines_added": 10,
      "lines_removed": 0,
      "net_change": 10
    }
  ]
}
aggregate:
    total_commits: 10
    total_lines_added: 30
    total_lines_removed: 15
    total_developers: 3
    active_developers: 1
    estimated_total_developers: 3
    estimated_active_developers: 1
    analysis_period_ticks: 310
    project_bus_factor: 2
    total_languages: 2
developers:
    - id: 0
      name: alice
      email: alice@example.com
      commits: 6
      lines_added: 16
      lines_removed: 0
      lines_changed: 0
      net_lines: 16
busfactor:
    - language: Go
      bus_factor: 2
      total_contributors: 3
      primary_dev_id: 2
      primary_dev_name: carol
      primary_percentage: 48.64864864864865
      secondary_dev_id: 0
      secondary_dev_name: alice
      risk_level: LOW

Fields:

Field Meaning
aggregate.total_commits Commits counted (merges included, empty commits only with --empty-commits).
aggregate.total_developers, active_developers Exact counts; active = with commits in the last 90 days of the window.
aggregate.estimated_total_developers, estimated_active_developers The same cardinalities estimated by a HyperLogLog sketch (precision 14, about 0.8 % error); equal to the exact counts on small inputs.
aggregate.analysis_period_ticks The newest tick of the window.
aggregate.project_bus_factor Smallest number of developers covering 50 % of all contributions (added + removed).
developers[] Sorted by commits (id on a tie): email is omitted when unknown; languages is null in JSON ([] in YAML) for a developer with no line statistics; first_tick / last_tick / active_ticks describe the active period.
languages[] Sorted by total_lines (lines added): total_contribution is added + removed; contributors maps developer id → contribution.
busfactor[] Per language, highest risk first: bus_factor, total_contributors, the primary and secondary owners (the secondary is present whenever the language has two or more contributors — developer id 0 included), primary_percentage / secondary_percentage, risk_level.
activity[], churn[] Per tick, with the tick's first and last committer time: commits per developer / lines added, removed and the net change.

NDJSON and timeseries

--format ndjson writes one record per commit with line statistics ({"commits", "lines_added", "lines_removed", "lines_changed", "author_id", "languages"}); --format timeseries lists every kept commit in tick order with the same entry under "devs".


See also