ImpactGate: Code Review via de Change Impact Measure

Website: https://impactgate.officefloor.net

Wat is structureel verval?

Structureel verval is de ophoping van complexiteit in bestaande structuren. Een voorbeeld hiervan is een 'god-method' die een extra vertakking krijgt, of een 'god-class' die een extra methode krijgt. De tool scoort een wijziging ten opzichte van een basislijn (standaard main) met de volgende change-impact formule:

impact = fileschanged Σ max(WMCother, 1) CC * Δlines (over gewijzigde functies)

Hierbij is WMC_other de complexiteit die al aanwezig is in de container die wordt bewerkt, gemeten in de staat vóór de wijziging. Het importeren van een volledig nieuw bestand of een nieuwe klasse is daarom "goedkoop", omdat er nog niets aanwezig was. Het toevoegen van code aan een reeds zware klasse is daarentegen "duur". Dit vormt het signaal voor structureel verval.

Voor de theoretische onderbouwing van deze formule kan worden verwezen naar het artikel Measuring the Blast Radius of Change op de OfficeFloor-blog.

Wanneer de impact te hoog is, vraagt de tool om de wijziging te vereenvoudigen of de betreffende code te refactoren. De tool kan worden ingesteld om enkel te waarschuwen (rapportage) of om de wijziging volledig te blokkeren (de build laten falen).

Installatie

Via pip:

pip install impact-gate         # installeert het `impact-gate` commando

Via Docker (zonder installatie): Gebruik de gepubliceerde image (git is inbegrepen; mount de repo naar /repo om te kunnen scoren):

docker run --rm -v "$PWD:/repo" ghcr.io/officefloor/impact-gate \
score --mode range --base origin/main

Voor lokale ontwikkeling:

python -m venv .venv && . .venv/bin/activate
pip install -e '.[dev]'         # editable install inclusief test-dependencies

Gebruik

CLI Commando's

  • Staged wijzigingen (pre-commit): Vergelijking tussen staged en HEAD (standaard).

``bash impact-gate score ``

  • Niet-gecommitteerde lokale wijzigingen: Vergelijking tussen working tree en HEAD.

``bash impact-gate score --mode worktree ``

  • CI of PR review: Vergelijking tussen de committed branch en main (merge-base..HEAD).

``bash impact-gate score --mode range --base origin/main --format json ``

  • Drempelwaarden en handhaving instellen: (Kan ook in .impact-gate.yml).

``bash impact-gate score --warn-at 50000 --block-at 200000 --enforcement block ``

Exit codes:

  • 0: OK of waarschuwing (de wijziging is toegestaan).
  • 2: Geblokkeerd (impact is te hoog bij --enforcement block).
  • 1: Gebruiks- of omgevingsfout.

Rapportage en filtering

Elk rapport bevat een lijst met bestanden die in aanmerking komen voor refactoring, gerangschikt naar hun aandeel in de totale impact. Terwijl het totaalcijfer op wijzigingsniveau bepaalt of de commit door mag, wijst de ranglijst per bestand aan waar het verval zich concentreert. Zo wordt een bestand dat langzaam uitgroeit tot een 'god-class' zichtbaar voordat het daadwerkelijk wijzigingen blokkeert.

Bestanden waarvan de diff groter is dan maxdifflines (standaard 200.000) worden beschouwd als gegenereerde dumps of externe blobs. De tool slaat deze over om vervorming van de score en vertraging te voorkomen. Deze bestanden worden vermeld onder 'skipped'.

Git pre-commit hook

Om elke commit lokaal te controleren vóór de CI-fase:

# Installeert .git/hooks/pre-commit
impact-gate install-hook

Bij handhaving via .impact-gate.yml wordt een commit met een te hoge impact geblokkeerd. Bij de instelling warn (of off) wordt het rapport geprint maar gaat de commit door. Gebruik --force om een bestaande hook te overschrijven.

Gebruik met het pre-commit framework: Voeg de volgende definitie toe aan .pre-commit-config.yaml:

repos:
- repo: https://github.com/officefloor/ImpactGate
  rev: v0.3.0
  hooks:
    - id: impact-gate

Beoordeling via een distributie (De Curve)

Het instellen van een vaste drempelwaarde is lastig, omdat de impact van een typische wijziging sterk verschilt per taal en project. In plaats van een vast getal kan een wijziging worden beoordeeld op basis van het percentiel binnen een distributie.

Baseline maken: Bouw of ververs de impact-distributie van het project op basis van de merge-historie:

impact-gate baseline --base-ref main
# Schrijft naar .impact-gate-baseline.json

Beoordelen op percentiel:

impact-gate score --curve --warn-percentile 90 --block-percentile 98

De beoordeling combineert twee distributies:

  1. Seed prior: Per-taal percentieltabellen gebaseerd op een corpus van 20 open-source repo's.
  2. Project baseline: De eigen distributie van het project, gebaseerd op de merge-historie van de mainline.

De weging van het project wordt bepaald door $w = n / (n + K)$, waarbij $n$ het aantal gelande wijzigingen is en $K$ (curvepriorweight, standaard 200) de hoeveelheid historie is die nodig is om het project zwaarder te laten wegen dan de seed.

Configuratie via .impact-gate.yml

De configuratie kan in de root van de repository worden geplaatst:

warn_at: 50000          # Impact waarbij een waarschuwing wordt gegeven
block_at: 200000        # Impact waarbij de wijziging wordt geblokkeerd
enforcement: warn       # Opties: off, warn, of block.
tolerance: 1.0          # Vermenigvuldigingsfactor voor drempelwaarden (bijv. 1.1 is milder)
# measure_config: .impact-measure.yml   # Optioneel: voor ignore globs en taal-overrides

# Grading curve (percentiel-beoordeling)
curve_enabled: false           # Indien true, worden warn_at/block_at genegeerd
warn_percentile: 90            # Waarschuwing bij dit percentiel of hoger
block_percentile: 98           # Blokkeren bij dit percentiel of hoger
curve_prior_weight: 200        # K in w = n/(n+K)
baseline_file: .impact-gate-baseline.json   # Locatie van de baseline-cache

CLI-flags hebben prioriteit boven het configuratiebestand.

Integratie met CI/CD

GitHub Actions

Voeg een workflow toe aan de repository. De action scoort de PR-branch tegen de base-branch en plaatst een samenvatting. fetch-depth: 0 is vereist.

name: Change impact
on: pull_request
permissions:
  contents: read
  pull-requests: write         # Nodig voor het plaatsen van de PR-comment
jobs:
  impact:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0
      - uses: officefloor/ImpactGate@v0
        with:
          enforcement: warn

De score verschijnt in de job-summary en als een 'sticky comment' op de PR. In block mode faalt de job wanneer de drempelwaarde wordt overschreden.

GitLab CI

Gebruik de kant-en-klare job uit ci/gitlab-ci.yml of include deze remote:

include:
  - remote: 'https://raw.githubusercontent.com/officefloor/ImpactGate/v0/ci/gitlab-ci.yml'

Het draait op merge-request pipelines en gebruikt de gepubliceerde Docker image. Indien een GITLAB_TOKEN (met api scope) is ingesteld, wordt er een sticky note in de MR geplaatst.

Jenkins

Een pipeline snippet is beschikbaar in ci/Jenkinsfile. Dit draait de Docker image op een agent met Docker, scoort de wijziging tegen de target branch (standaard origin/main) en archiveert het rapport.

Roadmap

  • Core CLI: Score staged, worktree of range. Warn of block. Text, JSON, markdown. (Klaar)
  • GitHub Action: Composite action, job-summary rapport en sticky PR comment. (Klaar)
  • Baseline en grading curve: Project profiling en blending van seed-corpus met projectdistributie. (Klaar)
  • Distributie: pip install, Docker image en version-tagged Action (@v0). (Klaar)
  • CI-plugins: GitLab CI template en Jenkins pipeline snippet. (Klaar)
  • Hooks: Git pre-commit hook en ondersteuning voor pre-commit framework. (Klaar)
  • IDE: Integratie via LSP met een live indicator tijdens het programmeren. (Toekomst)