Ragex.Analysis.DependencyGraph (Ragex v0.11.0)

View Source

Dependency analysis for module and function relationships.

Analyzes the dependency graph stored in the knowledge graph to:

  • Detect circular dependencies
  • Calculate coupling metrics (afferent, efferent, instability)
  • Find unused modules
  • Identify God modules (high coupling)
  • Suggest decoupling opportunities

All analysis operates on the existing knowledge graph edges (:calls, :imports).

Summary

Functions

Calculates coupling metrics for all modules in the project.

Analyzes dependencies for all modules in the knowledge graph.

Analyzes a module comprehensively.

Calculates coupling metrics for a module.

Generates suggestions for decoupling the codebase.

Finds circular dependencies in the module dependency graph.

Finds "God modules" - modules with high coupling.

Finds modules that are not referenced by any other module.

Types

coupling_metrics()

@type coupling_metrics() :: %{
  afferent: non_neg_integer(),
  efferent: non_neg_integer(),
  instability: float()
}

function_ref()

@type function_ref() :: {:function, module(), atom(), non_neg_integer()}

module_name()

@type module_name() :: atom()

suggestion()

@type suggestion() :: %{
  type: atom(),
  severity: :low | :medium | :high,
  description: String.t(),
  entities: [term()],
  metadata: map()
}

Functions

all_coupling_metrics(opts \\ [])

@spec all_coupling_metrics(keyword()) ::
  {:ok, [{module_name(), coupling_metrics()}]} | {:error, term()}

Calculates coupling metrics for all modules in the project.

Returns a map of module => coupling_metrics.

Parameters

  • opts: Keyword list of options
    • :include_transitive - Include transitive dependencies (default: false)
    • :sort_by - Sort by :instability, :afferent, :efferent, or :name (default: :instability)
    • :descending - Sort in descending order (default: true)

Returns

  • {:ok, [{module, metrics}]} - List of module-metrics tuples, sorted

Examples

{:ok, all_metrics} = all_coupling_metrics(sort_by: :instability)

analyze_all_dependencies()

@spec analyze_all_dependencies() :: {:ok, map()} | {:error, term()}

Analyzes dependencies for all modules in the knowledge graph.

Convenience function that returns a comprehensive dependency analysis.

Examples

{:ok, analysis} = DependencyGraph.analyze_all_dependencies()
analysis.modules  # => %{MyModule => %{dependencies: [...], ...}}

analyze_module(module, opts \\ [])

@spec analyze_module(
  module_name(),
  keyword()
) :: {:ok, map()} | {:error, term()}

Analyzes a module comprehensively.

Provides complete dependency analysis for a single module including:

  • Coupling metrics (afferent, efferent, instability)
  • Direct dependencies and dependents
  • Circular dependency involvement
  • God module status
  • Function count and complexity indicators

Parameters

  • module: Module name atom
  • opts: Keyword list of options
    • :include_transitive - Include transitive dependencies (default: false)
    • :include_functions - Include function list (default: false)
    • :ai_insights - Use AI for architectural insights (default: from config)

Returns

  • {:ok, analysis} - Comprehensive module analysis map
  • {:error, reason} - Error if module not found or analysis fails

Analysis Map Structure

%{
  module: ModuleName,
  exists: true,
  coupling: %{afferent: 5, efferent: 3, instability: 0.375},
  dependencies: [ModuleA, ModuleB],
  dependents: [ModuleC, ModuleD],
  function_count: 12,
  in_cycles: [[ModuleA, ModuleB, ModuleName]],
  is_god_module: false,
  functions: [...] # if include_functions: true
}

Examples

{:ok, analysis} = analyze_module(MyModule)
{:ok, analysis} = analyze_module(MyModule, include_transitive: true, include_functions: true)

coupling_metrics(module, opts \\ [])

@spec coupling_metrics(
  module_name(),
  keyword()
) :: {:ok, coupling_metrics()} | {:error, term()}

Calculates coupling metrics for a module.

Coupling metrics:

  • Afferent coupling (Ca): Number of modules that depend on this module (incoming)
  • Efferent coupling (Ce): Number of modules this module depends on (outgoing)
  • Instability (I): Ce / (Ca + Ce) - ranges from 0 (stable) to 1 (unstable)

Parameters

  • module: Module name atom
  • opts: Keyword list of options
    • :include_transitive - Include transitive dependencies (default: false)

Returns

  • {:ok, metrics} - Coupling metrics map
  • {:error, reason} - Error if module not found or analysis fails

Examples

{:ok, metrics} = coupling_metrics(MyModule)
# => %{afferent: 5, efferent: 3, instability: 0.375}

decoupling_suggestions(opts \\ [])

@spec decoupling_suggestions(keyword()) :: {:ok, [suggestion()]} | {:error, term()}

Generates suggestions for decoupling the codebase.

Analyzes the dependency graph to find:

  • Circular dependencies that should be broken
  • God modules that should be split
  • Highly unstable modules that need stabilization
  • Unused modules that can be removed

Returns

  • {:ok, [suggestion]} - List of decoupling suggestions
  • {:error, reason} - Error if analysis fails

Examples

{:ok, suggestions} = decoupling_suggestions()

find_cycles(opts \\ [])

@spec find_cycles(keyword()) ::
  {:ok, [[module_name() | function_ref()]]} | {:error, term()}

Finds circular dependencies in the module dependency graph.

Returns a list of cycles, where each cycle is a list of module names forming a circular dependency. Uses depth-first search to detect cycles.

Parameters

  • opts: Keyword list of options
    • :min_cycle_length - Minimum cycle length (default: 2)
    • :scope - :module or :function (default: :module)
    • :limit - Maximum number of cycles to return (default: 100)

Returns

  • {:ok, [cycle]} - List of cycles found
  • {:error, reason} - Error if analysis fails

Examples

# Find all module-level cycles
{:ok, cycles} = find_cycles()

# Find function-level cycles (minimum length 3)
{:ok, cycles} = find_cycles(scope: :function, min_cycle_length: 3)

find_god_modules(threshold, opts \\ [])

@spec find_god_modules(
  non_neg_integer(),
  keyword()
) :: {:ok, [{module_name(), coupling_metrics()}]} | {:error, term()}

Finds "God modules" - modules with high coupling.

God modules are modules that have excessive dependencies or dependents, indicating potential design issues.

Parameters

  • threshold: Minimum total coupling (afferent + efferent) to be considered a God module
  • opts: Keyword list of options
    • :sort_by - Sort by :total, :afferent, :efferent, or :instability (default: :total)

Returns

  • {:ok, [{module, metrics}]} - List of God modules with their metrics

Examples

# Find modules with total coupling >= 20
{:ok, god_modules} = find_god_modules(20)

find_unused(opts \\ [])

@spec find_unused(keyword()) :: {:ok, [module_name()]} | {:error, term()}

Finds modules that are not referenced by any other module.

A module is considered unused if:

  • No functions in the module are called by other modules
  • The module is not imported/required/used by other modules
  • The module is not an entry point (tests, mix tasks, etc.)

Parameters

  • opts: Keyword list of options
    • :exclude_tests - Exclude test modules from results (default: true)
    • :exclude_mix_tasks - Exclude Mix tasks (default: true)

Returns

  • {:ok, [module_name]} - List of potentially unused modules
  • {:error, reason} - Error if analysis fails

Examples

{:ok, unused} = find_unused()