devs

package
v0.0.0-...-d1eea97 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 18, 2026 License: Apache-2.0, Apache-2.0 Imports: 25 Imported by: 0

README

Developers Analysis

Preface

Software is built by people. Understanding the activity and contributions of the development team is essential for project management, though it must be done with care to avoid misuse.

Problem

Managers and teams often lack visibility into the distribution of work:

  • "Who is the primary contributor to this project?"
  • "Is the workload distributed evenly?"
  • "Which languages is each developer working with?"

How analyzer solves it

The Devs analyzer computes high-level activity statistics for each developer. It tracks:

  • Number of commits.
  • Lines added, removed, and changed.
  • Breakdown of activity by programming language.

Historical context

Counting lines of code (LOC) and commits is one of the oldest forms of software metrics. While widely criticized as a measure of productivity (quality != quantity), it remains a valid measure of activity and impact.

Real world examples

  • Bus Factor: Identifying key developers whose absence would stall the project.
  • Language Expertise: Finding out who on the team is writing the most Go vs. Python code.

How analyzer works here

  1. Identity Merging: Uses the IdentityDetector to merge multiple emails/names for the same person (using .mailmap or heuristics).
  2. Diff Analysis: For every commit, it calculates the line stats (added/removed/changed).
  3. Aggregation: Aggregates these stats per author and per time interval (tick).
  4. Language Detection: Maps files to languages to provide a language-specific breakdown.

Limitations

  • LOC is not Productivity: This analyzer does not measure code quality or problem-solving value. A deletion of 1000 lines can be more valuable than an addition of 1000 lines.
  • Squashed Commits: Squashing commits can obscure individual contributions.

Metrics

The devs analyzer computes the following metrics. Each metric implements the Metric[In, Out] interface from pkg/metrics, making them reusable across output formats (plot, JSON, YAML).

developers

Type: list

Per-developer contribution statistics including commits, lines added/removed, language breakdown, and activity timeline. Developers are sorted by commit count.

Output fields:

  • id - Developer identifier
  • name - Developer name (from identity resolution)
  • commits - Total number of commits
  • lines_added - Total lines added
  • lines_removed - Total lines removed
  • lines_changed - Total lines changed (modifications)
  • net_lines - Net line change (added - removed)
  • languages - Map of language to line stats
  • first_tick - First active time period
  • last_tick - Last active time period
  • active_ticks - Number of periods with activity
languages

Type: list

Per-language contribution statistics showing total lines and contributor breakdown. Languages are sorted by total lines added.

Output fields:

  • name - Language name
  • total_lines - Total lines added in this language
  • contributors - Map of developer ID to lines added
bus_factor

Type: risk

Knowledge concentration risk per language. Measures how dependent each language's codebase is on individual contributors.

Risk levels:

  • CRITICAL - Single developer owns >= 90% of codebase
  • HIGH - Single developer owns >= 80%
  • MEDIUM - Single developer owns >= 60%
  • LOW - Single developer owns < 60%

Output fields:

  • language - Language name
  • primary_dev_id - ID of top contributor
  • primary_dev_name - Name of top contributor
  • primary_percentage - Percentage owned by primary
  • secondary_dev_id - ID of second contributor (if exists)
  • secondary_dev_name - Name of second contributor
  • secondary_percentage - Percentage owned by secondary
  • risk_level - Risk classification
activity

Type: time_series

Time-series of commit activity per tick, broken down by developer. Shows contribution velocity over the analysis period.

Output fields:

  • tick - Time period index
  • by_developer - Map of developer ID to commit count
  • total_commits - Total commits in this tick
churn

Type: time_series

Time-series of lines added and removed per tick. High churn may indicate refactoring, feature development, or instability.

Output fields:

  • tick - Time period index
  • lines_added - Lines added in this tick
  • lines_removed - Lines removed in this tick
  • net_change - Net line change (added - removed)
aggregate

Type: aggregate

Aggregate statistics across all developers and the analysis period.

Output fields:

  • total_commits - Total commits across all developers
  • total_lines_added - Total lines added
  • total_lines_removed - Total lines removed
  • total_developers - Number of unique developers
  • active_developers - Developers with commits in recent 30% of period
  • analysis_period_ticks - Number of time periods analyzed

Further plans

  • More nuanced metrics (e.g., "churn" vs. "productive code").

Documentation

Overview

Package devs provides devs functionality.

Index

Constants

View Source
const (
	ConfigDevsConsiderEmptyCommits = "Devs.ConsiderEmptyCommits"
	ConfigDevsAnonymize            = "Devs.Anonymize"
)

Configuration option keys for the devs analyzer.

View Source
const (
	ThresholdCritical = 90.0
	ThresholdHigh     = 80.0
	ThresholdMedium   = 60.0
)

Risk thresholds.

View Source
const (
	RiskCritical = "CRITICAL"
	RiskHigh     = "HIGH"
	RiskMedium   = "MEDIUM"
	RiskLow      = "LOW"
)

Risk level constants.

View Source
const ActiveThresholdRatio = 0.7

ActiveThresholdRatio defines what portion of analysis period counts as "recent".

Variables

View Source
var (
	ErrInvalidTicks      = errors.New("devs: invalid Ticks in report")
	ErrInvalidPeopleDict = errors.New("devs: invalid ReversedPeopleDict in report")
)

Error definitions for the devs analyzer.

Functions

func AggregateCommitsToTicks

func AggregateCommitsToTicks(
	commitDevData map[string]*CommitDevData,
	commitsByTick map[int][]gitlib.Hash,
) map[int]map[int]*DevTick

AggregateCommitsToTicks builds per-tick per-developer data from per-commit data grouped by the commits_by_tick mapping.

func GenerateChart

func GenerateChart(report analyze.Report) (components.Charter, error)

GenerateChart creates a stacked bar chart showing developer activity over time.

func GenerateDashboard

func GenerateDashboard(report analyze.Report, writer io.Writer) error

GenerateDashboard creates the developer analytics dashboard HTML.

func GenerateSections

func GenerateSections(report analyze.Report) ([]plotpage.Section, error)

GenerateSections returns the dashboard sections without rendering.

func RegisterDevPlotSections

func RegisterDevPlotSections()

RegisterDevPlotSections registers the plot section renderer for the devs analyzer. Called from HistoryAnalyzer.Initialize to avoid init().

func RegisterTickExtractor

func RegisterTickExtractor()

RegisterTickExtractor registers the devs analyzer's per-commit extractor with the unified time-series output system.

Types

type ActivityData

type ActivityData struct {
	Tick         int         `json:"tick"          yaml:"tick"`
	ByDeveloper  map[int]int `json:"by_developer"  yaml:"by_developer"`
	TotalCommits int         `json:"total_commits" yaml:"total_commits"`
}

ActivityData contains time-series activity for a single tick.

type ActivityMetric

type ActivityMetric struct {
	metrics.MetricMeta
}

ActivityMetric computes time-series commit activity.

func NewActivityMetric

func NewActivityMetric() *ActivityMetric

NewActivityMetric creates the activity metric.

func (*ActivityMetric) Compute

func (m *ActivityMetric) Compute(input *TickData) []ActivityData

Compute calculates activity time series from tick data.

type AggregateData

type AggregateData struct {
	TotalCommits        int `json:"total_commits"         yaml:"total_commits"`
	TotalLinesAdded     int `json:"total_lines_added"     yaml:"total_lines_added"`
	TotalLinesRemoved   int `json:"total_lines_removed"   yaml:"total_lines_removed"`
	TotalDevelopers     int `json:"total_developers"      yaml:"total_developers"`
	ActiveDevelopers    int `json:"active_developers"     yaml:"active_developers"`
	AnalysisPeriodTicks int `json:"analysis_period_ticks" yaml:"analysis_period_ticks"`
}

AggregateData contains summary statistics.

type AggregateInput

type AggregateInput struct {
	Developers []DeveloperData
	Ticks      map[int]map[int]*DevTick
}

AggregateInput is the input for aggregate computation.

type AggregateMetric

type AggregateMetric struct {
	metrics.MetricMeta
}

AggregateMetric computes summary statistics.

func NewAggregateMetric

func NewAggregateMetric() *AggregateMetric

NewAggregateMetric creates the aggregate metric.

func (*AggregateMetric) Compute

func (m *AggregateMetric) Compute(input AggregateInput) AggregateData

Compute calculates aggregate statistics.

type BusFactorData

type BusFactorData struct {
	Language         string  `json:"language"                       yaml:"language"`
	PrimaryDevID     int     `json:"primary_dev_id"                 yaml:"primary_dev_id"`
	PrimaryDevName   string  `json:"primary_dev_name"               yaml:"primary_dev_name"`
	PrimaryPct       float64 `json:"primary_percentage"             yaml:"primary_percentage"`
	SecondaryDevID   int     `json:"secondary_dev_id,omitempty"     yaml:"secondary_dev_id,omitempty"`
	SecondaryDevName string  `json:"secondary_dev_name,omitempty"   yaml:"secondary_dev_name,omitempty"`
	SecondaryPct     float64 `json:"secondary_percentage,omitempty" yaml:"secondary_percentage,omitempty"`
	RiskLevel        string  `json:"risk_level"                     yaml:"risk_level"`
}

BusFactorData contains knowledge concentration data for a language.

type BusFactorInput

type BusFactorInput struct {
	Languages []LanguageData
	Names     []string
}

BusFactorInput is the input for bus factor computation.

type BusFactorMetric

type BusFactorMetric struct {
	metrics.MetricMeta
}

BusFactorMetric computes knowledge concentration risk per language.

func NewBusFactorMetric

func NewBusFactorMetric() *BusFactorMetric

NewBusFactorMetric creates the bus factor metric.

func (*BusFactorMetric) Compute

func (m *BusFactorMetric) Compute(input BusFactorInput) []BusFactorData

Compute calculates bus factor risk from language data.

type ChurnData

type ChurnData struct {
	Tick    int `json:"tick"          yaml:"tick"`
	Added   int `json:"lines_added"   yaml:"lines_added"`
	Removed int `json:"lines_removed" yaml:"lines_removed"`
	Net     int `json:"net_change"    yaml:"net_change"`
}

ChurnData contains code churn for a single tick.

type ChurnMetric

type ChurnMetric struct {
	metrics.MetricMeta
}

ChurnMetric computes time-series code churn.

func NewChurnMetric

func NewChurnMetric() *ChurnMetric

NewChurnMetric creates the churn metric.

func (*ChurnMetric) Compute

func (m *ChurnMetric) Compute(input *TickData) []ChurnData

Compute calculates churn time series from tick data.

type CommitDevData

type CommitDevData struct {
	Commits   int                              `json:"commits"`
	Added     int                              `json:"lines_added"`
	Removed   int                              `json:"lines_removed"`
	Changed   int                              `json:"lines_changed"`
	AuthorID  int                              `json:"author_id"`
	Languages map[string]pkgplumbing.LineStats `json:"languages,omitempty"`
}

CommitDevData holds aggregate dev stats for a single commit.

type ComputedMetrics

type ComputedMetrics struct {
	Ticks      map[int]map[int]*DevTick `json:"-"          yaml:"-"`
	TickSize   time.Duration            `json:"-"          yaml:"-"`
	Aggregate  AggregateData            `json:"aggregate"  yaml:"aggregate"`
	Developers []DeveloperData          `json:"developers" yaml:"developers"`
	Languages  []LanguageData           `json:"languages"  yaml:"languages"`
	BusFactor  []BusFactorData          `json:"busfactor"  yaml:"busfactor"`
	Activity   []ActivityData           `json:"activity"   yaml:"activity"`
	Churn      []ChurnData              `json:"churn"      yaml:"churn"`
}

ComputedMetrics holds all computed metric results for the devs analyzer. This is populated by running each metric's Compute method.

func ComputeAllMetrics

func ComputeAllMetrics(report analyze.Report) (*ComputedMetrics, error)

ComputeAllMetrics runs all devs metrics and returns the results.

func (*ComputedMetrics) AnalyzerName

func (m *ComputedMetrics) AnalyzerName() string

AnalyzerName returns the analyzer identifier.

func (*ComputedMetrics) ToJSON

func (m *ComputedMetrics) ToJSON() any

ToJSON returns the metrics in JSON-serializable format.

func (*ComputedMetrics) ToYAML

func (m *ComputedMetrics) ToYAML() any

ToYAML returns the metrics in YAML-serializable format.

type DashboardData

type DashboardData struct {
	Metrics      *ComputedMetrics
	TopLanguages []string // Top N language names for radar chart.
}

DashboardData wraps ComputedMetrics with rendering-specific data.

type DevTick

type DevTick struct {
	pkgplumbing.LineStats

	Languages map[string]pkgplumbing.LineStats
	Commits   int
}

DevTick is the statistics for a development tick and a particular developer.

type DeveloperData

type DeveloperData struct {
	ID          int                              `json:"id"            yaml:"id"`
	Name        string                           `json:"name"          yaml:"name"`
	Commits     int                              `json:"commits"       yaml:"commits"`
	Added       int                              `json:"lines_added"   yaml:"lines_added"`
	Removed     int                              `json:"lines_removed" yaml:"lines_removed"`
	Changed     int                              `json:"lines_changed" yaml:"lines_changed"`
	NetLines    int                              `json:"net_lines"     yaml:"net_lines"`
	Languages   map[string]pkgplumbing.LineStats `json:"languages"     yaml:"languages"`
	FirstTick   int                              `json:"first_tick"    yaml:"first_tick"`
	LastTick    int                              `json:"last_tick"     yaml:"last_tick"`
	ActiveTicks int                              `json:"active_ticks"  yaml:"active_ticks"`
}

DeveloperData contains computed data for a single developer.

type DevelopersMetric

type DevelopersMetric struct {
	metrics.MetricMeta
}

DevelopersMetric computes per-developer statistics.

func NewDevelopersMetric

func NewDevelopersMetric() *DevelopersMetric

NewDevelopersMetric creates the developers metric.

func (*DevelopersMetric) Compute

func (m *DevelopersMetric) Compute(input *TickData) []DeveloperData

Compute calculates developer statistics from tick data.

type HistoryAnalyzer

type HistoryAnalyzer struct {
	Identity  *plumbing.IdentityDetector
	TreeDiff  *plumbing.TreeDiffAnalyzer
	Ticks     *plumbing.TicksSinceStart
	Languages *plumbing.LanguagesDetectionAnalyzer
	LineStats *plumbing.LinesStatsCalculator

	ConsiderEmptyCommits bool
	Anonymize            bool
	// contains filtered or unexported fields
}

HistoryAnalyzer calculates per-developer line statistics across commit history.

func (*HistoryAnalyzer) ApplySnapshot

func (d *HistoryAnalyzer) ApplySnapshot(snap analyze.PlumbingSnapshot)

ApplySnapshot restores plumbing state from a snapshot.

func (*HistoryAnalyzer) Boot

func (d *HistoryAnalyzer) Boot() error

Boot restores the analyzer from hibernated state. Re-initializes the merges map for the next chunk.

func (*HistoryAnalyzer) CPUHeavy

func (d *HistoryAnalyzer) CPUHeavy() bool

CPUHeavy returns false because developer stats aggregation is lightweight bookkeeping.

func (*HistoryAnalyzer) CheckpointSize

func (d *HistoryAnalyzer) CheckpointSize() int64

CheckpointSize returns an estimated size of the checkpoint in bytes.

func (*HistoryAnalyzer) Configure

func (d *HistoryAnalyzer) Configure(facts map[string]any) error

Configure configures the analyzer with the given facts.

func (*HistoryAnalyzer) Consume

func (d *HistoryAnalyzer) Consume(_ context.Context, ac *analyze.Context) error

Consume processes a single commit with the provided dependency results.

func (*HistoryAnalyzer) Description

func (d *HistoryAnalyzer) Description() string

Description returns a human-readable description of the analyzer.

func (*HistoryAnalyzer) Descriptor

func (d *HistoryAnalyzer) Descriptor() analyze.Descriptor

Descriptor returns stable analyzer metadata.

func (*HistoryAnalyzer) Finalize

func (d *HistoryAnalyzer) Finalize() (analyze.Report, error)

Finalize completes the analysis and returns the result.

func (*HistoryAnalyzer) Flag

func (d *HistoryAnalyzer) Flag() string

Flag returns the CLI flag for the analyzer.

func (*HistoryAnalyzer) Fork

Fork creates a copy of the analyzer for parallel processing.

func (*HistoryAnalyzer) FormatReport

func (d *HistoryAnalyzer) FormatReport(report analyze.Report, writer io.Writer) error

FormatReport writes the formatted analysis report to the given writer.

func (*HistoryAnalyzer) GenerateChart

func (d *HistoryAnalyzer) GenerateChart(report analyze.Report) (components.Charter, error)

GenerateChart creates a chart for the history analyzer.

func (*HistoryAnalyzer) GenerateDashboardForAnalyzer

func (d *HistoryAnalyzer) GenerateDashboardForAnalyzer(report analyze.Report, writer io.Writer) error

GenerateDashboardForAnalyzer creates the full dashboard for this analyzer.

func (*HistoryAnalyzer) GenerateSections

func (d *HistoryAnalyzer) GenerateSections(report analyze.Report) ([]plotpage.Section, error)

GenerateSections returns the dashboard sections for combined reports.

func (*HistoryAnalyzer) Hibernate

func (d *HistoryAnalyzer) Hibernate() error

Hibernate compresses the analyzer's state to reduce memory usage. Clears the merges map since processed merge commits won't be seen again during streaming (commits are processed chronologically).

func (*HistoryAnalyzer) Initialize

func (d *HistoryAnalyzer) Initialize(_ *gitlib.Repository) error

Initialize prepares the analyzer for processing commits.

func (*HistoryAnalyzer) ListConfigurationOptions

func (d *HistoryAnalyzer) ListConfigurationOptions() []pipeline.ConfigurationOption

ListConfigurationOptions returns the configuration options for the analyzer.

func (*HistoryAnalyzer) LoadCheckpoint

func (d *HistoryAnalyzer) LoadCheckpoint(dir string) error

LoadCheckpoint restores the analyzer state from the given directory.

func (*HistoryAnalyzer) Merge

func (d *HistoryAnalyzer) Merge(branches []analyze.HistoryAnalyzer)

Merge combines results from forked analyzer branches.

func (*HistoryAnalyzer) Name

func (d *HistoryAnalyzer) Name() string

Name returns the name of the analyzer.

func (*HistoryAnalyzer) ReleaseSnapshot

func (d *HistoryAnalyzer) ReleaseSnapshot(_ analyze.PlumbingSnapshot)

ReleaseSnapshot is a no-op for devs (no UAST resources).

func (*HistoryAnalyzer) SaveCheckpoint

func (d *HistoryAnalyzer) SaveCheckpoint(dir string) error

SaveCheckpoint writes the analyzer state to the given directory.

func (*HistoryAnalyzer) SequentialOnly

func (d *HistoryAnalyzer) SequentialOnly() bool

SequentialOnly returns true because devs' Fork() does not isolate mutable map state.

func (*HistoryAnalyzer) Serialize

func (d *HistoryAnalyzer) Serialize(result analyze.Report, format string, writer io.Writer) error

Serialize writes the analysis result to the given writer.

func (*HistoryAnalyzer) SnapshotPlumbing

func (d *HistoryAnalyzer) SnapshotPlumbing() analyze.PlumbingSnapshot

SnapshotPlumbing captures the current plumbing state.

func (*HistoryAnalyzer) StateGrowthPerCommit

func (d *HistoryAnalyzer) StateGrowthPerCommit() int64

StateGrowthPerCommit returns the estimated per-commit memory growth in bytes.

type IdentityAuditEntry

type IdentityAuditEntry struct {
	CanonicalName string
	CommitCount   int
}

IdentityAuditEntry represents a developer identity for auditing.

func GenerateIdentityAudit

func GenerateIdentityAudit(report analyze.Report) []IdentityAuditEntry

GenerateIdentityAudit creates an audit list of developer identities.

type LanguageData

type LanguageData struct {
	Name         string      `json:"name"         yaml:"name"`
	TotalLines   int         `json:"total_lines"  yaml:"total_lines"`
	Contributors map[int]int `json:"contributors" yaml:"contributors"`
}

LanguageData contains computed data for a programming language.

type LanguagesMetric

type LanguagesMetric struct {
	metrics.MetricMeta
}

LanguagesMetric computes per-language statistics.

func NewLanguagesMetric

func NewLanguagesMetric() *LanguagesMetric

NewLanguagesMetric creates the languages metric.

func (*LanguagesMetric) Compute

func (m *LanguagesMetric) Compute(developers []DeveloperData) []LanguageData

Compute calculates language statistics from developer data.

type TickData

type TickData struct {
	Ticks    map[int]map[int]*DevTick
	Names    []string
	TickSize time.Duration
}

TickData is the raw input data for devs metrics computation.

func ParseTickData

func ParseTickData(report analyze.Report) (*TickData, error)

ParseTickData extracts TickData from an analyzer report.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL