DYVE|TECH Latest
News

Why Your AI Coding Agent Keeps Breaking Production (And How git blame Can Stop It)

Why Your AI Coding Agent Keeps Breaking Production (And How git blame Can Stop It): AI coding age...

Why Your AI Coding Agent Keeps Breaking Production (And How git blame Can Stop It): AI coding agents are getting remarkably good at writing software

More from Dyve Tech →


AI coding agents are getting remarkably good at writing software.

They can inspect a repository, understand a task, modify multiple files, run tests, fix errors, open pull requests, and sometimes even work through an entire feature with surprisingly little human intervention.

For developers using tools such as Claude Code, Cursor, Windsurf, GitHub Copilot, and other agentic coding systems, this can feel like having another engineer sitting beside you.

And that is exactly where the problem begins.

Because there is one thing your AI coding agent can know extremely well but still understand incorrectly:

the code that already exists.

An AI agent can read a function and understand what it appears to do.

It can identify duplicated logic.

It can recognize an old-looking API.

It can spot a strange setTimeout().

It can see a retry value of 7 and decide that 3 looks more reasonable.

It can replace a complicated block with a cleaner abstraction.

It can make the code look better.

It can make the tests pass.

And it can still break production.

That is not necessarily because the model is “bad at coding.”

The deeper problem is missing historical context.

The code in front of the agent is the final result of hundreds of decisions made by developers, incidents, customer complaints, infrastructure limitations, third-party API behaviour, browser bugs, database constraints, rushed releases, and lessons learned the hard way.

That history is often not written in the code.

It is written in Git.

And that makes commands such as git blame and git log -L surprisingly powerful tools for AI-assisted software engineering.



AI coding agents have a context problem

There is an important distinction between understanding code and understanding why the code exists.

An LLM can look at this:

MAX_RETRIES = 7

and understand what it means.

But it cannot automatically know why the number is 7.

Maybe the original developer chose it randomly.

Maybe it came from an old Stack Overflow answer.

Or maybe, two years ago, your payment provider started returning intermittent gateway errors and somebody increased the retry window after a production incident.

Those two situations look identical in the current source code.

The history is what separates them.

This matters even more now because AI-assisted development is becoming normal. Stack Overflow’s 2025 Developer Survey reported that 84% of respondents were using or planning to use AI tools in their development process, while 69% of AI-agent users said agents had increased their productivity. At the same time, the survey shows that AI agents are not universally trusted or universally adopted. (Stack Overflow Developer Survey)

The productivity gains are real.

But increased coding velocity creates another problem:

there is now more code being changed, and it can be changed faster than humans can understand it.

Recent research into AI-assisted code review has similarly highlighted the importance of contextual understanding and human oversight. One large study of 278,790 code-review conversations across 300 open-source projects found that human reviewers provided additional feedback around understanding, testing, and knowledge transfer, while many AI suggestions were not adopted because they were incorrect or developers used alternative fixes. (arXiv)

The answer is not to stop using AI.

The better answer is to give the agent better sources of context.



The real problem: AI sees the code, but not the battle scars

Most software systems contain code that looks strange for a reason.

Some of it is genuinely bad code.

Some of it is technical debt.

And some of it is what engineers sometimes call load-bearing legacy code.

That last category is dangerous.

It is code that looks unnecessary until you remove it.

Then everything falls apart.

A useful mental model is Chesterton’s Fence:

Before removing something that looks unnecessary, understand why it was put there.

Imagine an AI agent encounters this:

setTimeout(() => {

  initializeWidget();

}, 0);

The agent may reasonably think:

“Why delay this by one event-loop tick? I can call initializeWidget() directly.”

From a purely local perspective, that may look like a legitimate improvement.

But perhaps that delay was introduced after a browser rendering race.

Perhaps a DOM node must exist before initialization.

Perhaps another callback has to finish first.

Perhaps a third-party library mutates the DOM during the current task.

Perhaps someone spent six hours debugging a problem that only appeared on certain Android devices.

The source code does not necessarily tell the story.

The Git history might.



This is particularly relevant to Nigerian developers

If you are building software in Nigeria, this problem can become even more practical because many products operate in environments with constraints that are difficult to encode into generic “best practices.”

A developer building a fintech application may have to deal with:

  • payment-provider retries
  • intermittent network connections
  • webhook delays
  • inconsistent third-party responses
  • mobile-first traffic
  • low-bandwidth users
  • infrastructure cost constraints
  • regional service dependencies
  • SMS delivery delays
  • authentication edge cases
  • asynchronous payment confirmation
  • bank or gateway response variations

A developer building an e-commerce platform may encounter something completely different:

  • customers switching between WhatsApp and the website
  • payment confirmation arriving later than the checkout request
  • inventory changing between checkout and payment
  • unreliable client networks
  • users refreshing pages repeatedly
  • image-heavy mobile traffic
  • third-party delivery integrations

And a developer working on a Nigerian startup may be doing all of this with a very small engineering team.

There may not be a senior engineer available to explain why an apparently ridiculous piece of code exists.

The original developer might have left the company.

The Slack conversation might be gone.

The Jira ticket might be archived.

The documentation might never have been written.

But the Git commit can still be there.

That is why Git history is more than version control.

It is institutional memory.



Three ways an AI refactor can quietly break production

1. The “unnecessary” delay

Suppose an AI agent sees:

setTimeout(() => {

  refreshLayout();

}, 0);

It removes the timeout because calling the function directly appears simpler.

The unit tests pass.

The linter passes.

The code review looks cleaner.

Then a production-only UI race appears.

Maybe it happens only on a particular browser.

Maybe only on slower devices.

Maybe only when network data arrives at exactly the wrong moment.

The agent did not introduce a syntax error.

It introduced a context error.



2. The magic-number cleanup

Consider:

max_retries = 7

An AI agent may see 7 and think:

“Three retries is more conventional.”

It changes the value:

max_retries = 3

The application still starts.

The tests still pass.

Nothing looks obviously broken.

Then an external service becomes temporarily unavailable.

The application retries three times, gives up, and drops an operation that previously had enough time to recover.

If that operation happens to be a payment webhook, background job, notification, or synchronization process, the consequences can be much larger than the code change suggests.

The number was not “magic.”

It was undocumented operational knowledge.



3. The duplicated validation check

An AI notices:

if (!user) {

  return;

}


await loadUserData();


if (!user) {

  return;

}

The second check looks redundant.

So the agent removes it.

That might be perfectly safe.

Or it might not.

If loadUserData() changes state asynchronously, invokes another service, or creates a race between two execution paths, the second validation may exist because someone previously discovered a failure mode.

Again, the important question is not:

“Does this code look redundant?”

The important question is:

“Why was it added?”



Enter 

git blame

This is where one of Git’s oldest commands becomes surprisingly useful in AI-assisted development.

git blame path/to/file.js

git blame shows which commit last changed each line, along with information about the author and commit. GitHub exposes essentially the same concept through its Blame view, where developers can inspect line-by-line revision history and follow a line back to the relevant commit. (GitHub Docs)

For example:

a91f4c21 John Doe 2025-11-18 42) const MAX_RETRIES = 7;

Now the agent knows that line 42 came from commit a91f4c21.

That gives it somewhere to go next.

git show a91f4c21

Git’s git show command can display the commit message and textual diff for a commit, which is exactly what an agent needs when investigating why a line was introduced or changed. (Git)

The agent may discover:

Increase payment retries after gateway timeout incident


Payment provider occasionally returns temporary gateway errors

during high-volume periods.


Increase retries from 3 to 7.

Suddenly the “magic number” is no longer magic.

It has a history.



But 

git blame

is only the first step

There is an important technical distinction here.

git blame answers:

“Which commit last changed this line?”

git log -L can answer a deeper question:

“How did this particular line or function evolve over time?”

Git documents -L specifically for tracing the evolution of a line range or function within a file. (Git)

For example:

git log -L 42,48:src/services/payment.py -3

Or, when Git can identify the function:

git log -L :process_payment:src/services/payment.py

This can show the changes that shaped the relevant code instead of forcing the AI to scan the entire repository history.

That distinction matters.

A good AI coding agent should not blindly run thousands of Git commands before touching anything.

It should investigate the history of the code it is about to change.



The better AI coding workflow

Instead of:

Understand task

   ↓

Read code

   ↓

Change code

   ↓

Run tests

   ↓

Create PR

a production-conscious AI agent should work more like this:

Understand task

   ↓

Locate code

   ↓

Is this existing logic?

   ↓

   YES

   ↓

Inspect git blame

   ↓

Inspect relevant commit

   ↓

Trace line/function history with git log -L

   ↓

Identify intentional constraints

   ↓

Plan modification

   ↓

Change code

   ↓

Run tests

   ↓

Review diff

   ↓

Run relevant regression checks

   ↓

Create PR

That is a small change in workflow.

But conceptually, it is huge.

The agent is no longer treating the repository as merely a collection of files.

It is treating it as a historical system.



The “history before refactor” rule

If you build custom AI coding agents, this is a rule worth putting directly into the agent’s instructions:

Before modifying existing production code, inspect its history.


1. Identify the exact function, block, or lines being modified.


2. Use git blame to identify the commits that introduced or last

  changed the relevant lines.


3. Inspect the relevant commits with git show.


4. Use git log -L when the evolution of a function or line range

  needs deeper investigation.


5. Look for evidence of:

  - bug fixes

  - production incidents

  - race-condition fixes

  - API workarounds

  - rate-limit handling

  - browser-specific fixes

  - database constraints

  - security fixes

  - backwards compatibility

  - performance workarounds

  - customer-impacting regressions


6. Do not remove or change an apparent constraint simply because

  it looks unnecessary.


7. If the historical reason is important, preserve the behavior

  and document the constraint.


8. Explain in the execution plan how the existing behavior will

  remain safe after the refactor.

This does not mean the AI must ask permission every time it sees old code.

The objective is not to make the agent timid.

The objective is to make it context-aware.



A production example

Imagine the agent is working on:

def process_payment(payload):

  max_retries = 7


  for attempt in range(max_retries):

    response = gateway.charge(payload)


    if response.ok:

      return response


    time.sleep(2)


  raise PaymentError("Payment failed")

A conventional refactoring agent might propose:

MAX_RETRIES = 3

and perhaps extract the retry mechanism into a generic utility.

Looks cleaner.

But before doing that, the agent runs:

git blame -L 42,48 src/services/payment.py

It discovers the relevant commit:

b4f19a0 Tech Lead 2026-02-11

Then:

git show b4f19a0

The commit message says:

Increase payment retries after gateway timeout incident


Gateway occasionally takes longer to recover during traffic spikes.

Keep seven attempts to avoid dropping delayed payment callbacks.

Now the correct engineering decision is obvious.

The agent should not “clean up” the seven retries simply because three is more conventional.

It should preserve the behavior.

If the constraint is important enough, the code could become:

# Seven retries are intentional. The payment gateway can temporarily

# delay responses during traffic spikes. See commit b4f19a0.

MAX_RETRIES = 7

Now the next developer — human or AI — has context.



Why this matters even when your tests are green

One of the most dangerous assumptions in AI-assisted development is:

“The tests passed, so the refactor is safe.”

Not necessarily.

Tests answer questions about the behavior that you have encoded and exercised.

They do not automatically capture:

  • undocumented operational constraints
  • production-only traffic patterns
  • third-party service behaviour
  • rare race conditions
  • browser-specific behaviour
  • infrastructure quirks
  • historical compatibility requirements
  • customer workflows nobody thought to write tests for
  • deployment-specific configuration
  • timing-sensitive failures

This is especially relevant for smaller engineering teams.

A Nigerian startup might have one developer maintaining the backend, another handling frontend work, and a founder jumping into the repository when something breaks.

The test suite may be decent.

But the company’s real engineering knowledge may still live in people’s heads.

When those people are unavailable, Git history becomes extremely valuable.



AI doesn’t need more code context. Sometimes it needs historical context.

There is a tendency to solve AI coding problems by giving the model more files.

“Read the whole repository.”

“Read every documentation file.”

“Read the entire GitHub issue.”

“Load the whole codebase into context.”

That can help, but it is not always the best approach.

More context is not automatically better context.

If the agent is modifying ten lines of a payment service, the most valuable information might be:

  1. The ten lines themselves.
  2. The commit that introduced them.
  3. The commit immediately before the change.
  4. The reason recorded in the commit message.
  5. The issue or incident associated with that commit.
  6. The tests that were added alongside the fix.

That is surgical context.

Git is particularly good at providing it.



git log -L

is a surgical history tool

Suppose the agent wants to refactor this function:

def process_payment(payload):

  ...

Instead of dumping the repository’s entire history into the model, it can ask Git to trace that function:

git log -L :process_payment:src/services/payment.py

Git’s documentation supports function-based -L ranges as well as explicit line ranges. (Git)

For a smaller range:

git log -L 42,65:src/services/payment.py

The agent can then inspect a limited number of relevant commits.

For example:

git log -3 -- src/services/payment.py

followed by:

git show <commit>

This is much more targeted than asking the AI to consume the complete history of a large repository.



A practical history-aware agent pipeline

A production-grade AI coding workflow could implement the following sequence.

Step 1: Determine what the agent is about to change

The agent should first identify:

  • file
  • function
  • class
  • line range
  • dependency boundaries
  • tests covering the behavior

For example:

src/services/payment.py

process_payment()

lines 42-65



Step 2: Check whether the code already existed

If the agent created the code in the current task, historical inspection may be unnecessary.

If the code predates the current task, history becomes relevant.

This avoids wasting time investigating every newly created line.



Step 3: Run 

git blame

git blame -L 42,65 src/services/payment.py

Look for the commits associated with important lines.



Step 4: Inspect those commits

git show <commit>

Read both:

  • the commit message
  • the actual diff

The message tells you what the developer said they were solving.

The diff tells you what they actually changed.

You want both.



Step 5: Trace the evolution when necessary

If the reason is unclear:

git log -L 42,65:src/services/payment.py

This lets the agent follow the evolution of the relevant section rather than scanning unrelated history.



Step 6: Search for related tests

After discovering that a strange line was introduced to fix a specific problem, the agent should search for tests added around the same commit.

For example:

git show --stat <commit>

and then inspect the changed test files.

This is where historical context becomes extremely powerful.

The test may explain the bug better than the production code does.



Don’t blindly trust commit messages either

There is another important caveat.

Git history is not automatically correct.

Developers write bad commit messages.

Sometimes the message is:

fix stuff

or:

updates

or:

final-final-fix

You cannot build a reliable AI engineering workflow around commit titles alone.

The agent should correlate multiple signals:

  • git blame
  • git log -L
  • git show
  • commit diff
  • tests
  • issue references
  • documentation
  • comments
  • surrounding code
  • current runtime behaviour

If the commit message says:

cleanup payment service

but the diff contains a subtle retry change and a newly added regression test, the diff and test are much stronger evidence than the commit title.



When the agent should NOT stop

History-aware development should not turn into AI paralysis.

The goal is not:

“Never modify old code.”

That would be ridiculous.

The goal is:

“Understand consequential old code before modifying it.”

For example, the agent does not need an extensive historical investigation for:

  • adding a new file
  • adding a new component
  • fixing a typo
  • formatting-only changes
  • updating documentation
  • changing an unused variable name
  • straightforward test additions
  • generated files

The trigger should become stronger when the agent is:

  • deleting existing logic
  • changing retry behaviour
  • changing authentication logic
  • changing payment flows
  • changing database queries
  • changing asynchronous execution
  • changing caching
  • changing security checks
  • changing error handling
  • removing “weird” workarounds
  • changing third-party integrations
  • changing production configuration

These are the areas where historical context can have the highest value.



A useful rule: strange code deserves an explanation before deletion

One of the simplest instructions you can give an AI coding agent is:

If existing code looks unnecessarily complicated, investigate before simplifying it.

Not every ugly line is technical debt.

Sometimes it is an encoded production lesson.

Consider:

await new Promise(resolve => setTimeout(resolve, 50));

An AI sees an arbitrary 50ms delay.

A human sees a suspicious 50ms delay.

Neither should immediately delete it.

The correct question is:

Why 50ms?

Run:

git blame path/to/file.js

Then:

git show <commit>

If the answer is:

Workaround for race condition in vendor SDK v2.3

you have just prevented a regression.



This also changes how developers should write commits

There is a lesson here for human engineers too.

If AI agents are going to use Git history as a source of engineering context, commit quality becomes part of your AI infrastructure.

Compare:

fix bug

with:

Keep 7 payment retries for delayed gateway responses


The gateway can temporarily return timeout responses during traffic

spikes. Reducing retries causes callbacks to be abandoned before the

provider recovers.


Related incident: PAY-309

The second commit is dramatically more valuable.

Not just to humans.

To future AI agents.

A good commit message is effectively a tiny piece of machine-readable institutional memory.


Write comments for future agents, not just future humans

There is also a case for strategic comments.

Do not comment obvious code.

This:

# Set retries to 7

MAX_RETRIES = 7

adds almost nothing.

But this:

# Keep at 7: payment gateway may recover after transient timeout

# bursts. Lower values previously caused dropped callbacks.

# See commit b4f19a0.

MAX_RETRIES = 7

is useful.

The comment explains why.

That is the information an AI agent is least likely to infer from the code itself.



Git history can become an AI memory layer

This leads to a much bigger idea.

We often talk about giving AI agents:

  • vector databases
  • documentation
  • embeddings
  • project instructions
  • memory files
  • issue trackers
  • architecture documents

But one of the most valuable sources of project memory is already sitting inside most repositories:

Git history.

Every commit can potentially contain:

  • what changed
  • who changed it
  • when it changed
  • why it changed
  • what problem it solved
  • what code was affected
  • what tests were introduced

Git is not just a rollback mechanism.

It is a chronological record of engineering decisions.

An AI agent that knows how to interrogate that history can operate with considerably more context than an agent that only reads the current working tree.

Research on agentic programming has identified persistent memory and context management as important challenges for autonomous software-engineering systems. Git history is not a complete solution to that problem, but it is a particularly useful form of project-specific memory because it is already tied directly to the code being modified. (arXiv)



The Nigeria-specific advantage: small teams can preserve knowledge

This matters beyond AI.

Many Nigerian software teams are lean.

A developer may build a product from MVP to production with very few people.

Then:

  • the developer changes jobs
  • another developer takes over
  • a contractor joins
  • the startup grows
  • an AI coding agent gets introduced
  • nobody remembers why certain decisions were made

That is exactly when historical context becomes valuable.

You do not need a giant engineering organisation to benefit.

Even a two-person startup can establish the rule:

Important production decisions must be recorded in Git.

That one practice can save enormous amounts of time later.



What this means for developers using Cursor, Claude Code, Windsurf and Copilot

You do not need to abandon AI coding tools.

You need to change how you use them.

Instead of telling an agent:

“Refactor this service and make it cleaner.”

Try:

“Refactor this service. Before changing existing production logic, inspect Git history for the functions you intend to modify. Use git blame to identify relevant commits and git log -L to understand how critical sections evolved. Preserve intentional constraints and explain any historical behaviour that affects your refactor.”

That instruction is much more precise.

You are not asking the model to be “smarter.”

You are giving it a better engineering process.



A stronger system prompt for autonomous coding agents

For teams building custom agents, the policy can be even more explicit:

PRODUCTION CODE SAFETY RULE


Before modifying existing production code:


1. Identify the exact function, class, or line range being changed.


2. Determine whether the code predates the current task.


3. For existing logic, run:

  git blame -L <start>,<end> <file>


4. Inspect relevant commits:

  git show <commit>


5. If the reason for the code is unclear, trace its evolution:

  git log -L <start>,<end>:<file>


6. Look specifically for:

  - production incidents

  - regression fixes

  - race conditions

  - security fixes

  - API workarounds

  - rate-limit handling

  - compatibility requirements

  - database constraints

  - browser/device-specific fixes

  - performance workarounds

  - customer-impacting bugs


7. Do not remove behaviour solely because it appears redundant,

  outdated, unusual, or inconsistent with a preferred pattern.


8. Before refactoring, identify the invariant that the existing

  code is protecting.


9. Preserve that invariant in the new implementation.


10. If the historical reason is important and undocumented,

  add a concise comment explaining the constraint.


11. After modifying the code:

  - inspect git diff

  - run relevant tests

  - run regression tests where available

  - verify that the original behaviour remains intact


12. In the final report, explicitly state:

  - what historical context was discovered

  - which constraints were preserved

  - what tests were executed

  - what assumptions remain uncertain

This is much closer to how a senior engineer should supervise an autonomous coding agent.



The bigger lesson: faster coding needs better memory

AI has changed the bottleneck in software development.

For years, one of the biggest problems was:

How quickly can we write the code?

AI is dramatically improving that.

The next question is:

How safely can we change the code?

That is different.

When a developer spends three hours implementing a feature, they naturally build some understanding of the surrounding system.

An AI agent can make a similar change in minutes.

That speed is useful.

But it means the development process needs stronger guardrails.

Git history is one of those guardrails.

Not because Git is intelligent.

Because Git remembers.



Don’t make your AI agent afraid of legacy code

There is a temptation to go too far with this idea.

You might instruct your agent:

Never modify code written by someone else.

That solves the problem by creating another problem.

Legacy code has to be changed.

Old workarounds eventually become unnecessary.

Dependencies get upgraded.

Architecture evolves.

Security requirements change.

The correct principle is not:

“Legacy code is sacred.”

It is:

“Legacy code is evidence.”

Investigate it before replacing it.

If the historical reason is still valid, preserve it.

If the historical reason is obsolete, remove it deliberately.

If nobody knows why it exists, increase your testing and investigation before touching it.

That is engineering judgment.



A simple checklist before letting an AI refactor production code

Before approving an AI-generated refactor, ask:

  • Did the agent identify the exact code being changed?
  • Did it inspect git blame for important existing lines?
  • Did it inspect the relevant commits?
  • Did it use git log -L when the evolution was unclear?
  • Did it look for regression fixes?
  • Did it look for production incidents?
  • Did it check related tests?
  • Did it preserve intentional timing, retry, caching, security, or compatibility behaviour?
  • Did it inspect the final git diff?
  • Did the relevant tests pass?
  • Did the agent explain why the refactor is safe?

If the answer to all of these is yes, you have substantially improved the quality gate around AI-generated code.



The future of AI coding is not just better models

The obvious direction is to keep building smarter models.

Better reasoning.

Larger context windows.

Better code generation.

Better agents.

But reliable AI software engineering will also depend on something less glamorous:

better engineering workflows.

A model can be incredibly capable and still make a bad decision if it lacks the context needed to make that decision.

Giving an AI coding agent access to Git history does not magically make it infallible.

It does something more practical.

It gives the agent a way to ask:

“Why is this code like this?”

before deciding:

“I know how to make this cleaner.”

That distinction could be the difference between a successful refactor and a 2 a.m. production incident.



Final takeaway

AI coding agents are becoming powerful enough to modify real production systems at remarkable speed.

That is an opportunity.

But speed without historical context can create a new class of engineering failures: confident refactors that are technically reasonable but operationally wrong.

The solution is not to stop using AI.

It is to make AI behave more like an experienced engineer.

Before changing important existing code:

git blame

to find out who changed it.

Then:

git show <commit>

to understand what that change did.

And when you need to understand how a function or line range evolved:

git log -L

to trace its history.

Git’s documentation explicitly supports line-range and function-level history tracing through -L, while git blame provides line-level attribution that can lead you to the relevant commits. (Git)

The most important question an AI coding agent can ask before rewriting legacy code is not:

“Can I make this cleaner?”

It is:

“Why was this written this way in the first place?”

Because sometimes the ugliest line in the repository is not technical debt.

Sometimes it is the scar left by the last production incident.

And if your AI agent learns to read those scars before touching the code, you give it something every good engineer needs:

institutional memory.