Don’t Let Your Git Repository Become an AI “Battlefield”: The Border Wars and Reconciliation of Vibe Coding
Last Wednesday at 11 PM, our release window was still open. Three engineers, three feature branches, the same codebase, and—three unrestrained AI coding assistants. When we finally mustered the courage to execute git merge, the conflict prompts on the screen seemed endless: UserService.ts had been rewritten four times, database.ts sprouted three different connection pool strategies, and that poor utility function file had lost any trace of its original code. Our CI red line filled the entire monitoring screen. I didn’t sleep that night.
This is the true face of unrestrained Vibe Coding in a team environment. One person using AI to “code by feel” is freedom; a group doing so is a silent war. Most teams’ disasters do not stem from poor AI coding but from AI’s inherent lack of boundaries. It instinctively optimizes any code it sees, turning parallel development into an unpredictable refactoring tournament.
This article will not provide you with a one-size-fits-all silver bullet, but I will candidly share the practical lessons learned from that disaster—ranging from physical isolation to time compression, and rethinking the old adage of “shared code.” If you are leading a team attempting Vibe Coding, this will be a survival report from the battlefield.
1. AI’s “Boundary-Crossing Instinct”: Why It Goes Out of Control
The core mechanism of AI coding is: given context, predict the most likely correct next step. When you open the entire codebase as context on a branch and tell the AI, “Help me tidy up the user validation logic,” it won’t just modify auth/validator.ts. It will scan the call chain, find that the User type definition can be improved in seven places; it notices that AuthService’s exception handling is inconsistent with new requirements; it thinks that database queries can be parameterized to prevent injection—even if you never mentioned it.
What it does is precisely what we taught it: to be a “responsible” developer. However, in parallel development, this “responsible” behavior turns into a full invasion of others’ work. Worse still, this is not just a text conflict issue— even if a piece of code does not generate <<<<<<< markers, AI on two branches may make semantically incompatible changes to the same interface: one changes the return value from User | null to Result<User, Error>, while another changes it to always return User and throw an exception when missing. The merged code may run, but the behavior is completely wrong. This semantic conflict can only be discovered at runtime, often resulting in the most painful bugs.
Thus, setting boundaries is not just to prevent Git conflict markers but to prevent “silent destruction.”
2. Physical Boundaries: Putting an Invisible Collar on AI
The most effective and brute-force method is to make AI unable to see the code it shouldn’t modify. Modern monorepo tools (Nx, Turborepo, Bazel) provide precise project dependency graphs and folder tags, which we can use to build a “context compartment.”
1. Enforce Folder-Level Permissions with Nx Tags and CI
Assuming your repository structure is as follows:
libs/
core/
user/ # Public user module
features/
checkout/ # Checkout feature
profile/ # Profile feature
Tag each library in nx.json:
{
"projects": {
"core-user": { "tags": ["scope:core", "type:shared"] },
"features-checkout": { "tags": ["scope:checkout", "type:feature"] },
"features-profile": { "tags": ["scope:profile", "type:feature"] }
}
}
Then write a simple CI rule (which can be a custom ESLint rule or a Shell script) that checks on every PR: If a branch claims to be developing the checkout feature, it should never modify files tagged as scope:core or scope:profile, unless it explicitly declares “public module modification” and tags the PR title with [CORE-CHANGE].
Here’s how we do it in GitHub Actions:
- name: Check for unauthorized cross-scope changes
run: |
CHANGED=$(git diff --name-only origin/main...HEAD)
for file in $CHANGED; do
if [[ $file == libs/core/* ]] && ! echo "$PR_TITLE" | grep -q "\[CORE-CHANGE\]"; then
echo "Error: Core module modified without [CORE-CHANGE] declaration."
exit 1
fi
done
This may sound strict, but it is this “physical interception” that first made the team feel safe: you know your AI assistant cannot quietly rewrite the shared User module without alerting anyone. The boundaries are secure.
2. Narrow the AI Tool’s Working Context
At the start of Vibe Coding conversations, clearly limit the context. In Cursor or Copilot Chat, we can do this through .cursorrules or custom instructions:
You can only modify files in the `src/features/checkout` directory.
If you need to modify anything under `src/core`, you must pause and ask: "I need to modify the core module [name], reason: [explanation]. Is this allowed?"
No out-of-bounds modifications may occur without explicit confirmation.
At the same time, only open the current feature folder as the workspace in the IDE to prevent AI from scanning the entire project. Although this requires some adjustment, it significantly reduces the probability of AI “accidentally” refactoring public dependencies. We observed that requests for out-of-bounds modifications decreased by nearly 70%.
3. Architectural Boundaries: Rethinking the Cost of “Shared Code”
This experience forced us to reflect: is all that “public code” causing endless conflicts really worth sharing?
In traditional engineering, DRY (Don’t Repeat Yourself) is a virtue. However, in the era of Vibe Coding, over-pursuing DRY can create highly coupled shared modules that become battlegrounds for every AI branch. A generic utility library optimized by five people using AI often incurs merging conflict costs far exceeding the cost of a few lines of repeated code.
We began to accept a “WET moderation” strategy: If a piece of logic can evolve independently within two business boundaries and has low stability, allow duplication. For example, if two different feature panels need a “format currency” function, we would previously extract it to shared/utils/formatCurrency.ts. Now we consider: do their future evolution directions truly align? Perhaps one will soon need multi-currency support, while the other needs cryptocurrency support. Rather than letting AI create conflicts in a shared file, it’s better to let them each have a copy and evolve freely within their domains.
We call this model the “isolation chamber” model: vertically segmenting each business capability, allowing its own state management, utility functions, and even partial entity definitions. The shared core is drastically compressed to only include truly stable, rarely changed elements: for instance, enterprise-level unified HTTP client configurations or design system token values. This way, the conflict area of public code decreases exponentially.
This is not a regression but a pragmatic choice based on team topology: let the code structure reflect the team’s communication structure (inverse Conway’s law). Vibe Coding amplifies each developer’s “productivity radius”; correspondingly, we must reduce physical dependencies between modules.
4. Temporal Boundaries: Nipping Conflicts in the Bud
After spatial isolation, the temporal dimension is equally important. Our second key shift is: from long-running feature branches to extremely short branches + Trunk-Based Development.
In Vibe Coding, AI can generate the amount of code that a regular developer would take a day to write in just one hour. This means that if your branch lives longer than eight hours, it may already be unrecognizable and diverged significantly from the main line. Our current rules are:
- The lifecycle of any feature branch should not exceed one day. If it takes longer than a day, the feature needs to be further split.
- Within a day, you must merge the main line into your branch at least three times. This high-frequency merging transforms one large conflict into several small ones, and the cognitive burden of resolving small conflicts is much lower.
- Use feature flags to merge incomplete features. Code entering the main line is not immediately exposed to users, allowing you to continue Vibe in the background.
We wrote a simple auxiliary script to run AI conflict simulations before merging the main line:
# Simulate merge, generate conflict preview
git merge-tree $(git merge-base HEAD origin/main) HEAD origin/main > /tmp/merge-preview.txt
# Let AI analyze the simulated merge conflicts and propose solutions
aicmd "Please analyze the simulated merge conflicts in /tmp/merge-preview.txt and provide resolution suggestions."
Thus, before the actual merge, we already have a semi-automated draft of solutions. Conflicts are no longer sudden accidents but a prepared negotiation.
5. Cultural Boundaries: Establishing the Role of “Public Module Guardian”
Finally, no matter how good the tools and processes are, if the team’s mindset does not change, they will not be sustainable. When everyone can use AI to refactor code like a god, the hesitation of “Can I change this?” will be overwhelmed by the immense productivity rush. We must consciously establish a role we call the “core guardian,” rotating weekly.
The guardian’s responsibility is not to prevent modifications but to arbitrate and accelerate changes to public code. When someone genuinely needs to modify a shared module (for example, to support a new interface), they do not need to wait for lengthy approvals; they just need to initiate a brief “boundary change intent” in the team channel:
“I need to add a
getUserPreferencesmethod to theUsermodule, with the following signature. It will be completed and merged within one hour. Guardian, please pay attention.”
The guardian will monitor this modification to ensure it stays within the contractual scope and notify other members to pull the latest changes after merging. Thus, changes to public modules are serialized and broadcasted, avoiding the disaster of parallel rewrites. Since each change is small and frequent, the waiting time is almost negligible.
We also added a “boundary check” to our daily stand-ups: quickly reviewing which branches might touch public modules today and coordinating the order verbally.
Conclusion: Dancing with AI, Not Fighting Against It
Since that terrifying night six months ago, our Git repository has not erupted into a hell of conflicts across hundreds of files. Not because we limited AI’s capabilities, but precisely the opposite: we provided it with a clearer, safer stage where it can flourish without stepping on others’ toes.
If you ask me what the solution to the boundary issues in multi-person Vibe Coding is, my answer is: make invisible boundaries visible constraints—through folders, tags, CI checkpoints, short branches, roles, and clear intent declarations, building fences in the physical, temporal, and social dimensions that everyone (including AI) clearly understands where “home” is and where “others’ territory” lies.
Don’t try to tame AI to prevent it from crossing boundaries; that is futile. What you should do is design a system it cannot cross. When boundaries are clear enough, Vibe can truly flow within the team without becoming the reason for weekly Wednesday night breakdowns.
Comments
Discussion is powered by Giscus (GitHub Discussions). Add
repo,repoID,category, andcategoryIDunder[params.comments.giscus]inhugo.tomlusing the values from the Giscus setup tool.