feature

package
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Dec 23, 2025 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package feature provides analysis features for tower visualizations.

Overview

Beyond basic rendering, Stacktower can analyze dependency graphs to surface insights about the software ecosystem. This package provides:

  • Nebraska ranking: Identify critical maintainers (inspired by XKCD #2347)
  • Brittle detection: Flag potentially unmaintained dependencies

Nebraska Ranking

The RankNebraska function identifies the maintainers whose packages are most critical to a project's foundation—the "Nebraska guys" from the famous XKCD comic about that one random person maintaining infrastructure everyone depends on.

The scoring algorithm considers:

  • Depth: Packages deeper in the tower (more dependencies above) score higher. These foundational packages have larger "blast radius" if abandoned.

  • Role weight: Owners get 3x weight, leads get 1.5x, regular maintainers get 1x. This reflects the difference in bus factor impact.

  • Shared credit: When multiple maintainers share a package, the depth score is divided among them, reflecting distributed responsibility.

Usage:

rankings := feature.RankNebraska(g, 10)  // Top 10 maintainers
for _, r := range rankings {
    fmt.Printf("%s (score: %.1f)\n", r.Maintainer, r.Score)
    for _, pkg := range r.Packages {
        fmt.Printf("  - %s (%s, depth %d)\n", pkg.Package, pkg.Role, pkg.Depth)
    }
}

Roles

The package recognizes three maintainer roles:

  • RoleOwner: The repository owner (highest weight)
  • RoleLead: First listed maintainer who isn't owner (medium weight)
  • RoleMaintainer: All other maintainers (standard weight)

Role information comes from node metadata (repo_owner, repo_maintainers) populated by the GitHub enrichment during dependency parsing.

Brittle Detection

The IsBrittle function identifies packages that may be at risk:

  • Archived repositories
  • No commits in over 2 years
  • Single maintainer with no recent activity

Brittle packages are highlighted in visualizations to draw attention to potential maintenance risks in the dependency tree.

Visualization Integration

These features integrate with the rendering pipeline:

rankings := feature.RankNebraska(g, 5)
svg := sink.RenderSVG(layout,
    sink.WithNebraska(rankings),  // Adds ranking panel
    sink.WithPopups(),            // Shows brittle warnings in popups
)

The SVG renderer adds interactive highlighting—hovering over a maintainer in the Nebraska panel highlights all their packages in the tower.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AsInt

func AsInt(v any) int

func CountMaintainers

func CountMaintainers(v any) int

func IsBrittle

func IsBrittle(n *dag.Node) bool

IsBrittle returns true if a node represents a package that is potentially unmaintained or risky to depend on. It checks for archived repositories, long periods of inactivity, and low maintainer counts.

Example
package main

import (
	"fmt"

	"github.com/matzehuels/stacktower/pkg/dag"
	"github.com/matzehuels/stacktower/pkg/render/tower/feature"
)

func main() {
	// Check if a package is potentially unmaintained
	node := &dag.Node{
		ID:  "old-package",
		Row: 1,
		Meta: dag.Metadata{
			"repo_archived":    true,
			"repo_last_commit": "2020-01-01",
		},
	}

	if feature.IsBrittle(node) {
		fmt.Println("Package is brittle (archived or unmaintained)")
	}
}
Output:

Package is brittle (archived or unmaintained)
Example (Healthy)
package main

import (
	"fmt"

	"github.com/matzehuels/stacktower/pkg/dag"
	"github.com/matzehuels/stacktower/pkg/render/tower/feature"
)

func main() {
	// Well-maintained package with recent activity
	node := &dag.Node{
		ID:  "active-package",
		Row: 1,
		Meta: dag.Metadata{
			"repo_archived":    false,
			"repo_last_commit": "2024-12-01", // Recent
			"repo_maintainers": []string{"dev1", "dev2", "dev3"},
			"repo_stars":       1000,
		},
	}

	if !feature.IsBrittle(node) {
		fmt.Println("Package appears healthy")
	}
}
Output:

Package appears healthy
Example (StaleWithFewMaintainers)
package main

import (
	"fmt"

	"github.com/matzehuels/stacktower/pkg/dag"
	"github.com/matzehuels/stacktower/pkg/render/tower/feature"
)

func main() {
	// Package with no recent activity and single maintainer
	node := &dag.Node{
		ID:  "stale-package",
		Row: 1,
		Meta: dag.Metadata{
			"repo_archived":    false,
			"repo_last_commit": "2022-01-01", // Over 1 year old
			"repo_maintainers": []string{"solo-dev"},
			"repo_stars":       50, // Low star count
		},
	}

	if feature.IsBrittle(node) {
		fmt.Println("Package may be at risk")
	}
}
Output:

Package may be at risk

func ParseDate

func ParseDate(v any) time.Time

Types

type NebraskaRanking

type NebraskaRanking struct {
	Maintainer string
	Score      float64
	Packages   []PackageRole
}

func RankNebraska

func RankNebraska(g *dag.DAG, topN int) []NebraskaRanking

RankNebraska identifies the most influential maintainers in the dependency graph using the Nebraska ranking algorithm. Maintainers are scored based on the "depth" of their packages in the tower (i.e., how many things depend on them).

Example
package main

import (
	"fmt"

	"github.com/matzehuels/stacktower/pkg/dag"
	"github.com/matzehuels/stacktower/pkg/render/tower/feature"
)

func main() {
	// Create a dependency graph with GitHub metadata
	g := dag.New(nil)

	// Root application (row 0)
	_ = g.AddNode(dag.Node{ID: "myapp", Row: 0})

	// Mid-level dependencies (row 1)
	_ = g.AddNode(dag.Node{
		ID:  "framework",
		Row: 1,
		Meta: dag.Metadata{
			"repo_owner":       "alice",
			"repo_maintainers": []string{"alice", "bob"},
			"repo_url":         "https://github.com/alice/framework",
		},
	})

	// Foundation packages (row 2) - deeper = more critical
	_ = g.AddNode(dag.Node{
		ID:  "http-client",
		Row: 2,
		Meta: dag.Metadata{
			"repo_owner":       "charlie",
			"repo_maintainers": []string{"charlie"},
			"repo_url":         "https://github.com/charlie/http-client",
		},
	})
	_ = g.AddNode(dag.Node{
		ID:  "json-parser",
		Row: 2,
		Meta: dag.Metadata{
			"repo_owner":       "alice",
			"repo_maintainers": []string{"alice", "dave"},
			"repo_url":         "https://github.com/alice/json-parser",
		},
	})

	// Add edges
	_ = g.AddEdge(dag.Edge{From: "myapp", To: "framework"})
	_ = g.AddEdge(dag.Edge{From: "framework", To: "http-client"})
	_ = g.AddEdge(dag.Edge{From: "framework", To: "json-parser"})

	// Rank the top 3 maintainers
	rankings := feature.RankNebraska(g, 3)

	for _, r := range rankings {
		fmt.Printf("%s (score: %.1f)\n", r.Maintainer, r.Score)
		for _, pkg := range r.Packages {
			fmt.Printf("  - %s (%s, depth %d)\n", pkg.Package, pkg.Role, pkg.Depth)
		}
	}
}
Output:

charlie (score: 6.0)
  - http-client (owner, depth 2)
alice (score: 4.5)
  - json-parser (owner, depth 2)
  - framework (owner, depth 1)
dave (score: 1.5)
  - json-parser (lead, depth 2)
Example (SharedResponsibility)
package main

import (
	"fmt"

	"github.com/matzehuels/stacktower/pkg/dag"
	"github.com/matzehuels/stacktower/pkg/render/tower/feature"
)

func main() {
	// Demonstrate how scores are divided among maintainers
	g := dag.New(nil)

	_ = g.AddNode(dag.Node{ID: "app", Row: 0})
	_ = g.AddNode(dag.Node{
		ID:  "critical-lib",
		Row: 1,
		Meta: dag.Metadata{
			"repo_owner": "alice",
			// Three maintainers share responsibility
			"repo_maintainers": []string{"alice", "bob", "charlie"},
		},
	})
	_ = g.AddEdge(dag.Edge{From: "app", To: "critical-lib"})

	rankings := feature.RankNebraska(g, 3)

	fmt.Println("Shared responsibility divides the score:")
	for _, r := range rankings {
		fmt.Printf("%s: %.2f\n", r.Maintainer, r.Score)
	}
}
Output:

Shared responsibility divides the score:
alice: 1.00
bob: 0.50
charlie: 0.33

type PackageRole

type PackageRole struct {
	Package string
	Role    Role
	URL     string
	Depth   int
}

type Role

type Role string
const (
	RoleOwner      Role = "owner"
	RoleLead       Role = "lead"
	RoleMaintainer Role = "maintainer"
)

Jump to

Keyboard shortcuts

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