noodlbox

Safe Refactoring with Dependency Analysis

Understand dependencies before refactoring code

Use dependency analysis to refactor code safely without breaking things.

Scenario

You need to refactor code and want to:

  • Find all usages
  • Understand impact
  • Avoid breaking changes
  • Update systematically

Tools & Resources

Refactoring Workflows

Workflow 1: Renaming a Function

Before renaming, find all callers:

MATCH (caller:CODE_SYMBOL)-[:CALLS]->(f:CODE_SYMBOL {name: "oldFunctionName"})
RETURN caller.name, caller.file_path, caller.line_number
ORDER BY caller.file_path

After renaming, verify impact:

{
  "repository": "my-app",
  "change_scope": "unstaged",
  "include_content": true
}

Workflow 2: Extracting a Function

Find all places using the code you're extracting:

// Find code that will call the new function
MATCH (s:CODE_SYMBOL)
WHERE s.name = "longFunction"
MATCH (s)-[:CALLS]->(dep:CODE_SYMBOL)
RETURN dep.name, dep.file_path

Workflow 3: Moving Code to a New Module

Understand current dependencies:

// What does this code depend on?
MATCH (s:CODE_SYMBOL {name: "featureToMove"})-[:CALLS]->(dep:CODE_SYMBOL)
RETURN dep.name, dep.file_path, dep.symbol_type

// Who depends on this code?
MATCH (caller:CODE_SYMBOL)-[:CALLS]->(s:CODE_SYMBOL {name: "featureToMove"})
RETURN caller.name, caller.file_path

Example: Refactoring Authentication

Scenario

Split monolithic auth.ts into separate modules.

Step 1: Analyze Current Structure

MATCH (s:CODE_SYMBOL)-[:CONTAINED_BY]->(f:File {path: "src/auth.ts"})
RETURN s.name, s.symbol_type
ORDER BY s.symbol_type, s.name

Step 2: Find Internal Dependencies

MATCH (s1:CODE_SYMBOL)-[:CONTAINED_BY]->(f:File {path: "src/auth.ts"})
MATCH (s1)-[:CALLS]->(s2:CODE_SYMBOL)
MATCH (s2)-[:CONTAINED_BY]->(f)
RETURN s1.name, s2.name

Step 3: Find External Dependencies

// What does auth.ts call outside itself?
MATCH (s1:CODE_SYMBOL)-[:CONTAINED_BY]->(f1:File {path: "src/auth.ts"})
MATCH (s1)-[:CALLS]->(s2:CODE_SYMBOL)
MATCH (s2)-[:CONTAINED_BY]->(f2:File)
WHERE f2.path <> "src/auth.ts"
RETURN DISTINCT f2.path, count(*) as calls
ORDER BY calls DESC

Step 4: Find Who Calls Auth

MATCH (caller:CODE_SYMBOL)-[:CALLS]->(callee:CODE_SYMBOL)
MATCH (callee)-[:CONTAINED_BY]->(f:File {path: "src/auth.ts"})
MATCH (caller)-[:CONTAINED_BY]->(callerFile:File)
WHERE callerFile.path <> "src/auth.ts"
RETURN DISTINCT callerFile.path, count(*) as calls
ORDER BY calls DESC

Step 5: Plan the Split

Based on query results:

  • Group related functions
  • Minimize cross-file calls
  • Maintain clear interfaces

Step 6: Make Changes

Split the file into modules.

Step 7: Verify Impact

{
  "repository": "my-app",
  "change_scope": "all",
  "max_processes": 50
}

Check affected processes and symbols.

Refactoring Patterns

Pattern 1: Find All Usages

MATCH (user:CODE_SYMBOL)-[r]->(target:CODE_SYMBOL {name: "targetFunction"})
RETURN user.name, user.file_path, type(r)
ORDER BY user.file_path

Pattern 2: Analyze Call Depth

// How deep is the call chain?
MATCH path = (start:CODE_SYMBOL {name: "entry"})-[:CALLS*]->(end:CODE_SYMBOL)
RETURN length(path) as depth, [n in nodes(path) | n.name] as chain
ORDER BY depth DESC
LIMIT 10

Pattern 3: Find Circular Dependencies

MATCH path = (s:CODE_SYMBOL)-[:CALLS*2..5]->(s)
RETURN [n in nodes(path) | n.name] as cycle
LIMIT 5

Tips

Before Refactoring

  1. Map all dependencies - Know what you're changing
  2. Check cross-community impact - Wide impact = more risk
  3. Identify entry points - These can't change signature
  4. Look for test coverage - Tests show usage patterns

During Refactoring

  1. Make small changes - Easier to verify
  2. Check impact after each change - Catch issues early
  3. Update tests first - Tests guide refactoring
  4. Preserve interfaces - Internal changes are safer

After Refactoring

  1. Verify no unexpected impacts - Run impact detection
  2. Check processes still work - Trace key processes
  3. Review cross-flows - Ensure architecture improves

On this page