Search documentation of Ragex

# Ragex.Analysis.Impact (Ragex v0.4.0)

View Source

Change impact analysis using graph traversal and metrics.

Predicts the impact of code changes by analyzing:

- Call graph (who calls this function?)
- Betweenness centrality (how critical is this node?)
- PageRank (how important is this node?)
- Complexity metrics (how complex is the code?)

## Usage

    alias Ragex.Analysis.Impact

    # Analyze impact of changing a function
    {:ok, analysis} = Impact.analyze_change({:function, MyModule, :process, 2})

    # Find tests affected by a change
    {:ok, tests} = Impact.find_affected_tests({:function, MyModule, :process, 2})

    # Estimate refactoring effort
    {:ok, estimate} = Impact.estimate_effort(:rename_function, {:function, MyModule, :old, 2})

    # Calculate risk score
    {:ok, risk} = Impact.risk_score({:function, MyModule, :critical, 1})

## Summary

### Types

effort_estimate()
impact_analysis()
node_ref()
risk_analysis()
test_ref()

### Functions

analyze_change(target, opts \\ [])

Analyzes the impact of changing a function or module.

estimate_effort(operation, target, opts \\ [])

Estimates the effort required for a refactoring operation.

find_affected_tests(target, opts \\ [])

Finds tests that would be affected by changing a function.

risk_score(target, opts \\ [])

Calculates a risk score for changing a function or module.

## Types

# effort_estimate()

    @type effort_estimate() :: %{
      operation: atom(),
      target: node_ref(),
      estimated_changes: non_neg_integer(),
      complexity: :low | :medium | :high | :very_high,
      estimated_time: String.t(),
      risks: [String.t()],
      recommendations: [String.t()]
    }

# impact_analysis()

    @type impact_analysis() :: %{
      target: node_ref(),
      direct_callers: [node_ref()],
      all_affected: [node_ref()],
      affected_count: non_neg_integer(),
      depth: non_neg_integer(),
      risk_score: float(),
      importance: float(),
      recommendations: [String.t()]
    }

# node_ref()

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

# risk_analysis()

    @type risk_analysis() :: %{
      target: node_ref(),
      importance: float(),
      coupling: float(),
      complexity: float(),
      overall: float(),
      level: :low | :medium | :high | :critical,
      factors: map()
    }

# test_ref()

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

## Functions

# analyze_change(target, opts \\ [])

    @spec analyze_change(
      node_ref(),
      keyword()
    ) :: {:ok, impact_analysis()} | {:error, term()}

Analyzes the impact of changing a function or module.

Uses graph traversal to find all code that would be affected by changing the target node. Calculates risk scores and provides recommendations.

## Parameters

- `target` - Node reference (function or module tuple)
- `opts` - Keyword list of options
  - `:depth` - Maximum traversal depth (default: 5)
  - `:include_tests` - Include test files in analysis (default: true)
  - `:exclude_modules` - List of modules to exclude

## Returns

- `{:ok, impact_analysis}` - Impact analysis result
- `{:error, reason}` - Error if analysis fails

## Examples

    {:ok, analysis} = Impact.analyze_change({:function, MyModule, :process, 2})
    IO.puts("Functions affected: " <> Integer.to_string(analysis.affected_count))
    IO.puts("Risk score: " <> Float.to_string(analysis.risk_score))

# estimate_effort(operation, target, opts \\ [])

    @spec estimate_effort(atom(), node_ref(), keyword()) ::
      {:ok, effort_estimate()} | {:error, term()}

Estimates the effort required for a refactoring operation.

## Parameters

- `operation` - Refactoring operation atom
  - `:rename_function` - Rename a function
  - `:rename_module` - Rename a module
  - `:extract_function` - Extract code into new function
  - `:inline_function` - Inline a function
  - `:move_function` - Move function to another module
  - `:change_signature` - Change function signature
- `target` - Target node reference
- `opts` - Additional options for specific operations

## Returns

- `{:ok, effort_estimate}` - Effort estimation
- `{:error, reason}` - Error if estimation fails

## Examples

    {:ok, estimate} = Impact.estimate_effort(:rename_function, {:function, MyModule, :old, 2})
    IO.puts("Estimated changes: " <> Integer.to_string(estimate.estimated_changes))
    IO.puts("Complexity: " <> Atom.to_string(estimate.complexity))
    IO.puts("Time: " <> estimate.estimated_time)

# find_affected_tests(target, opts \\ [])

    @spec find_affected_tests(
      node_ref(),
      keyword()
    ) :: {:ok, [test_ref()]} | {:error, term()}

Finds tests that would be affected by changing a function.

Traverses the call graph to find test functions that directly or indirectly call the target function.

## Parameters

- `target` - Function reference tuple
- `opts` - Keyword list of options
  - `:depth` - Maximum traversal depth (default: 10)
  - `:test_patterns` - Patterns to identify test modules (default: ["Test", "_test"])

## Returns

- `{:ok, [test_ref]}` - List of affected test functions
- `{:error, reason}` - Error if analysis fails

## Examples

    {:ok, tests} = Impact.find_affected_tests({:function, MyModule, :process, 2})
    IO.puts("Tests affected: " <> Integer.to_string(length(tests)))

# risk_score(target, opts \\ [])

    @spec risk_score(
      node_ref(),
      keyword()
    ) :: {:ok, risk_analysis()} | {:error, term()}

Calculates a risk score for changing a function or module.

Combines multiple factors:

- Importance (PageRank score)
- Coupling (number of dependencies)
- Complexity (if available from quality metrics)

## Parameters

- `target` - Node reference
- `opts` - Keyword list of options
  - `:weights` - Custom weights for factors (default: equal)

## Returns

- `{:ok, risk_analysis}` - Risk analysis with score 0.0-1.0
- `{:error, reason}` - Error if scoring fails

## Examples

    {:ok, risk} = Impact.risk_score({:function, MyModule, :critical, 1})
    IO.puts("Overall risk: " <> Float.to_string(risk.overall) <> " (" <> Atom.to_string(risk.level) <> ")")