Search documentation of Ragex

# Ragex.Analysis.DependencyGraph (Ragex v0.4.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

### Types

coupling_metrics()
function_ref()
module_name()
suggestion()

### Functions

all_coupling_metrics(opts \\ [])

Calculates coupling metrics for all modules in the project.

analyze_all_dependencies()

Analyzes dependencies for all modules in the knowledge graph.

analyze_module(module, opts \\ [])

Analyzes a module comprehensively.

coupling_metrics(module, opts \\ [])

Calculates coupling metrics for a module.

decoupling_suggestions(opts \\ [])

Generates suggestions for decoupling the codebase.

find_cycles(opts \\ [])

Finds circular dependencies in the module dependency graph.

find_god_modules(threshold, opts \\ [])

Finds "God modules" - modules with high coupling.

find_unused(opts \\ [])

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()