Skip to content

# Bug: Same-named classes across modules silently overwrite each other in the graph #1744

Description

@aviciot

Graphify version: 0.9.9


Hey team 👋

We're running Graphify on a large multi-module Maven project (15 modules, ~700 Java files) and hit a frustrating silent bug,

What happened

We have two completely separate classes both named FinancialEntryValidator — one in payment-adaptor and one in core-adaptor. Different packages, different logic, different purpose.

my-project/
  payment-adaptor/src/main/java/com/example/payment/service/FinancialEntryValidator.java
  core-adaptor/src/main/java/com/example/core/service/FinancialEntryValidator.java

When Graphify builds the graph, both get the same short node ID (financialentryvalidator). Since G.add_node() is idempotent, whichever gets processed last silently overwrites the other. The losing class completely disappears from the graph — no node, no edges, nothing.

There's no warning, no error, no indication anything went wrong.
The graph looks complete.- But when we asked our LLM "does the payment module validate currency against ISO 4217?" it answered confidently: no validation exists :(

The real answer is the opposite — the check is right there in the class that got overwritten:

// payment-adaptor - FinancialEntryValidator.java
private static final Set<String> ISO_CURRENCIES =
    Currency.getAvailableCurrencies().stream()
        .map(Currency::getCurrencyCode)
        .collect(Collectors.toSet());

if (!ISO_CURRENCIES.contains(currency)) {
    validationErrors.add("Currency [" + currency + "] is invalid ISO 4217 alpha-3 code");
}

Silent wrong answers are worse than no answers.

Minimal reproduction

import networkx as nx

G = nx.DiGraph()

# Module A processes first — real node with full source_file
G.add_node("financialentryvalidator",
    label="FinancialEntryValidator",
    source_file="payment-adaptor/.../FinancialEntryValidator.java"
)

# Module B processes second — stub node overwrites the real one
G.add_node("financialentryvalidator",
    label="FinancialEntryValidator",
    source_file=""   # stub — empty
)

# Real node is gone
print(G.nodes["financialentryvalidator"]["source_file"])  # "" — overwritten

What we did as a workaround

We added a post-processing step after build_merge that detects stub nodes (empty source_file) colliding with real nodes and redirects their edges to the correct real node. It resolved about half of the stubs in one repo .
But it can't resolve cases where multiple real classes share the same label — those stay broken and the LLM keeps getting wrong answers for those classes.

Same root cause — short names used as IDs without preserving module/package context. PR #1697 addresses call edge ambiguity for same-name methods.
This issue is about node identity — the class itself disappearing. Both need fixing, but they're separate problems.

Suggested fix direction

Node IDs for real classes (those with a known source_file) should incorporate the module/package path, not just the short class name. For example:

# Current — collision
financialentryvalidator

# Better — unique per module
payment_adaptor.financialentryvalidator
core_adaptor.financialentryvalidator

Stub nodes (import references with no source_file) can keep short IDs since they're placeholders anyway — and would correctly resolve to the right real node once IDs are unique.

let me know if more details or test a fix if that helps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions