Refactoring Shared Functions Safely When Call Sites Span Dozens of Autonomous Teams
Code search and task decomposition make cross-team refactors manageable.

Enterprise code search is cross-repository search and navigation that lets engineers locate definitions and consumers across an organization's codebase. For safely refactoring shared functions, the essential capabilities are cross-repository references, structural search, and context that engineers and AI agents can query. These capabilities support the central requirement of a cross-team refactor: coordinating consumer migrations.
Why shared-function refactoring is a coordination problem first
A shared-function refactor looks simple until the engineer changing it discovers how many teams call it. The edit may be small, but each consumer belongs to a release train that the owning team does not control. Correctness alone cannot settle the migration schedule. The work reaches into other teams' sprint plans, including teams that never agreed to participate.
OpenHands' decomposition framework identifies treating a large refactor as a monolithic task as a root cause of stalled work. Agents and junior engineers need bounded tasks that can be validated independently, while consuming teams need changes they can absorb on their own schedules. Three jobs need their own attention: choosing a target worth changing, changing its consumers, and keeping quality high when test coverage and release cadences differ. Each begins with knowing where the dependencies are.
Finding every call site before touching anything
An incomplete call-site inventory makes reliable sequencing impossible. A function can live in a monorepo while its consumers occupy satellite repositories, or a shared library can serve services spread across independent Git hosts. Searching the repository that owns the definition does not establish the migration boundary. Consumers that go unseen are left out of both the technical plan and the communication plan.
For this task, enterprise code search needs more than fast text matching across millions of lines. Cross-repository jump-to-definition and find-all-references connect a shared function to consumers across every indexed repository. Structural search looks for AST patterns rather than strings, so it also finds call sites where the whitespace, aliasing, or import paths differ. A grep-based sweep can miss those cases entirely.
The discovery artifact must identify every consuming team, repository, file, and line that calls the function. Ownership belongs in the inventory. Without it, an engineer may know what needs editing but still lack the information needed to notify a team or sequence its migration. This map becomes the basis for task boundaries, progress tracking, and the final removal decision.
Deciding which shared functions are actually worth the disruption
Whether the disruption is worth it should depend on how the function affects delivery. A messy function that rarely changes may impose less cost than a mediocre function changed weekly by six teams. Change frequency combined with coupling exposes repeated merge conflicts, coordination overhead, and regression risk more clearly than raw complexity.
Risk also changes the confidence required before proceeding. Authentication flows, permission layers, compliance modules, and shared state need stronger checks that every call site has been found, because missing a consumer does more harm. These areas require a more dependable inventory and validation of the migration pattern on lower-risk consumers before touching them.
How to decompose the refactor so autonomous teams can absorb it incrementally
Once the target and its consumers are known, the work should be divided at team or service boundaries. Each unit should fit into a single commit or PR and have an independent acceptance test. Dependency analysis distinguishes tasks that can run in parallel from tasks that must wait. This is more precise than dividing the work by file count, because the boundary follows the component being changed.
The framework illustrates this with a language-version migration. Rather than assigning an agent to port the entire codebase, a task can specify updating deprecated ArrayList declarations in the user-service package and passing its tests. The scope is explicit and reviewable. In a shared-function migration, the equivalent unit identifies the consumer to transform and the checks that establish whether its use of the new interface is acceptable.
A rolling integration branch such as v2-api-refactor gives these units a shared place to go before they reach main. Each team or agent works on a sub-branch and opens a PR into that integration branch, preserving human review before production. Every task also needs a fixer, a verifier, and a rollback condition. The fixer defines the transformation, the verifier defines what must pass, and the rollback condition defines what triggers reversion. Together, they give a consuming team a bounded contract without requiring it to reconstruct the entire migration.
Sequencing the rollout across teams that didn't opt in
Independent release cadences require a period when both interfaces work. Stage 1 is parallel availability, in which the owning team ships the new function while keeping the old one operational. Consuming teams receive notice but are not yet required to migrate.
Stage 2 makes the pending change visible locally. The old function emits a warning through a log, lint rule, or CI annotation at each call site, putting the migration signal inside the consuming team's workflow. The owning team no longer has to send each reminder. References disappearing from the indexed call-site map show which consumers have moved, making the original discovery artifact a progress dashboard.
Stage 3 establishes a communicated calendar date for removing the old interface. Lower-risk consumers should migrate first, while authentication, compliance, and shared-state consumers migrate after the pattern has been validated. The deadline needs a way to enforce it without having to keep escalating. Removing the old symbol in the integration branch causes CI failures in repositories still calling it, making the unresolved dependency visible to the team that owns it. The inventory shows which consumers are left before the final integration reaches main.
Giving AI agents enough codebase context to execute the transformation reliably
A bounded task still fails if the agent cannot see the dependencies that define its boundary. A local file view can omit cross-repository callers, leaving an apparently correct edit that breaks consumers elsewhere. Manual correction then consumes the expected speed gain.
MCP, or Model Context Protocol, connects agents to external context. Claude Code and Cursor support MCP, allowing them to retrieve live cross-repository reference data. That connection supplies visibility, but the task still needs a precise contract. The agent should receive the function's current signature and semantics, every call site in scope for its task, the tests that must pass, and the consuming team's naming and style conventions. The reference map establishes where to work, while the verifier establishes when the work is acceptable.
Running AI agents in parallel without merge conflicts consuming the gains
The branching model makes parallel execution concrete. An agent works on a component sub-branch such as v2-refactor/service-a and creates a PR into v2-refactor, the integration branch. It does not target main. Agents resolve merge conflicts within the integration structure, while humans review the resulting PRs.
The integration branch is a review boundary. Each PR remains a single bounded change with its defined verifier, and the integration branch reaches main only after human review. This keeps agent output from flowing directly into production. It also gives reviewers a smaller decision than accepting an entire cross-repository transformation at once.
When agents work in parallel, their tasks need a visible status as well as separate branches. A platform lead needs to see which sub-branches are done, waiting for review, or stalled, so the amount of agent activity does not hide unfinished work. Human review becomes the limiting resource as agent output increases. Small, tested PRs let reviewers process that output without repeatedly reconstructing dependencies or separating unrelated edits.
Reducing the interrupt load the refactor places on the owning team
Cross-team migrations can turn the owning team into a help desk. A consuming team encounters an edge case, asks for an explanation, and interrupts an engineer who understands the shared function. The engineer answers verbally, and the answer is then lost. When the next team has the same question, the cycle starts again and takes attention away from finishing the refactor.
A queryable context layer gives those questions a shared destination. Engineers and non-engineers can ask about the function's behavior, its call sites, and the migration pattern without routing every question through a senior engineer. The indexed reference map supplies cross-repository context that makes answers to questions about a particular service specific to the caller.
Keeping sensitive code inside the organization's own infrastructure during the process
Shared functions often encode business rules, authentication logic, and integration contracts. Their sensitivity can block agent adoption when execution sends code context to an external model provider. Self-hosting addresses that blocker only when both execution and inference stay inside the organization's network. An open-source agent client running internally still transmits context externally if it calls a cloud-hosted model endpoint.
The endpoint is the decisive deployment detail here. Evaluating where the agent runs without evaluating where its model receives context leaves the data path unresolved. For a refactor spanning many repositories, that distinction applies to the shared code and the caller context supplied to the agent. Keeping both within the organization's infrastructure requires an internally hosted model endpoint as well as internally hosted execution.
In regulated environments, local processing comes with rules to meet as well as engineers' preferences. Defense contractors, financial services organizations, and healthcare organizations must follow rules on how they handle and transfer data under frameworks including FedRAMP High and GDPR. Cloud-hosted code intelligence tools may need to be configured specifically to meet those rules. The deployment decision must therefore account for the actual handling of code context rather than treating the label “self-hosted” as sufficient.
What a complete cross-team refactor rollout looks like end to end
The work starts by indexing every repository and building the target function's complete call-site map. That artifact must connect references to owning teams before the migration schedule is set.
The next decision is whether change frequency and coupling justify the disruption, followed by identifying high-risk consumers and sequencing migrations from lower to higher risk. Authentication, compliance, and shared-state consumers wait until the transformation pattern has been validated elsewhere.
The owning team then ships the new interface while retaining the old one and publishes the migration guide, verifier, and rollback condition. Consuming teams get a change with a clear scope instead of being asked to work out what needs changing.
Soft deprecation puts warnings into each team's own pipeline through lint rules or CI annotations. This makes the pending cutoff visible without requiring the owning team to repeat the notification manually.
Agents working in parallel then each get a bounded set of files, context with the dependencies resolved, and the verifier for that task. Agents submit PRs into the integration branch, where humans review and merge the changes.
The live reference index shows how far the migration has gone. As deprecated references disappear, the remaining call-site map identifies the consumers that still need to migrate.
At the final cutoff, the old interface is removed in the integration branch, so CI failures show which consumers still call it before integration reaches main. Cross-repository search, queryable context, and MCP-connected agents remain available for the next refactor, reducing the discovery and explanation work that would otherwise begin again.


