Why Git Is Not Enough for AI-Generated Code
Git tells you what changed. You need to know why. With AI code, the why—reasoning, constraints, alternatives—is what matters. Git alone leaves you flying blind.
Definition
Git is a version control system optimized for tracking code changes: what lines were added, removed, or modified, by whom, and when. It's essential for collaborative development, but it captures only a narrow slice of reality. For AI-generated code, that narrow slice creates a governance gap: the version control system records the what, but not the why, and not the how-we-got-here. A complete record of AI decision-making requires information that git was never designed to store.
Why This Matters
Here's a concrete scenario. It's 3 AM on a Tuesday, and your database is returning stale data. You trace the bug to a caching layer that was generated by an AI agent a month ago. The TTL is set to 5 minutes when your data freshness SLA requires updates every 60 seconds.
You pull up the git blame and see:
a1b2c3d 2026-02-05 12:34:56 AI-Agent-Session-001: Add caching layer for user profilesThe commit message says: "Implement caching to reduce database load."
Now you have questions:
- Did the AI choose 5 minutes because it analyzed your data freshness requirements and determined that was appropriate?
- Did the AI copy the TTL from a different codebase where the SLA was different?
- Did the AI discover a performance constraint (database was overloaded at 60-second update intervals) and set the TTL to solve it?
- Did the AI misunderstand the requirement entirely?
Git has no answer. The commit message is generic. The author is "AI-Agent-Session-001," which doesn't exist anymore—it was a transient process that completed and terminated. You can't ask it questions. You can't see the prompt that generated the code. You can't see what the AI considered and rejected. You can't see the constraints it discovered. You can't verify that the reasoning was sound. This is why capturing reasoning behind code changes and building semantic context are essential for governance.
You're left with two options: reverse-engineer the reasoning from the code itself (which is slow and uncertain), or make an educated guess (which is how most incidents get handled). Either way, you've lost information that would have prevented the problem.
This is the core problem: git captures what changed, but for AI-generated code, you need to understand why it changed, what constraints shaped the decision, and what reasoning led to this specific implementation.
What Git Captures
Let's be precise about what version control systems actually record:
- Diff: Which lines were added, removed, modified
- Metadata: Author, timestamp, branch, commit hash
- Commit message: A human-written summary (usually)
- Parent commit: The commit this build on
- Merge history: Which branches were combined
For human-written code, this is often sufficient because you can:
- Read the code and understand the implementation
- Ask the author why they made specific decisions
- Infer intent from context and style
- Have a conversation about trade-offs
Git's model works for human collaboration because humans are persistent: the author is still around to explain their reasoning.
What Git Cannot Capture
Here's what git cannot capture, but what you need for AI governance:
1. The Prompt and Context
What instruction generated this code?
Prompt (from git): [missing]
Actual prompt (from AI system):
"Write a function to cache user profile data with Redis.
Use a TTL of 5 minutes. Don't modify the schema.
Assume we're optimizing for response time, not data freshness.
The endpoint serves read-heavy traffic; writes are rare."The prompt tells you what the AI was asked to optimize for (response time, not freshness). If the prompt is wrong, the code is right given that prompt—but the real requirement is different.
Without the prompt, reviewers can't determine whether the code solves the right problem. They can only verify that it solves some problem.
2. The Reasoning Trace
How did the AI arrive at this decision?
For complex code, the AI might have considered multiple approaches:
- Approach A: In-process cache (fast, doesn't scale)
- Approach B: Redis cache (slower, scalable, requires network round-trip)
- Approach C: Database query optimization (cheapest, slowest)
Maybe the AI reasoned: "Approach A works for single-server deployments, but we have three app servers behind a load balancer, so we need shared cache state. Redis is the standard choice here."
Or maybe: "Approach B has more latency, but the profile endpoint is called ~100x per second. Saving 1ms per call saves 100ms total per second, which justifies the network hop."
The reasoning trace shows why this implementation was chosen over alternatives. It reveals the constraints the AI discovered and the trade-offs it evaluated.
Git has no place for this information. Commit messages are afterthoughts, written by humans, often generic.
3. Constraints Discovered During Implementation
As the AI writes code, it discovers constraints that shaped the output. These constraints aren't documented anywhere except in the AI's reasoning—which is ephemeral.
For example:
- "The user table doesn't have a last_updated timestamp, so cache invalidation requires a full table scan—I'm using 5 minutes as a safe lower bound"
- "The Redis client library doesn't support conditional set operations, so I'm using a two-step process: check existence, then set if missing"
- "We're hitting memory limits on our Redis instance, so I'm being conservative with cache sizes"
- "The auth layer caches tokens for 10 minutes, and I'm aligning this TTL with that decision for consistency"
These constraints are crucial context. They explain why the code is written the way it is. Without them, a reviewer trying to optimize for freshness might change the TTL without realizing they're breaking an intentional alignment with another system, or hitting a discovery about the schema that the AI already accounted for.
4. Alternatives Evaluated and Rejected
The AI might have explored five different caching strategies and chose one. The rejected ones are gone forever.
- Why not in-process cache? (Doesn't scale to multi-server)
- Why not write-through cache? (Breaks atomicity guarantees)
- Why not cache-aside pattern? (We tried it; the get-check-get race condition is hard to handle)
The alternatives matter because they show that the chosen approach was deliberate, not accidental. They also prevent future developers from "discovering" a rejected approach and thinking it's a clever new idea.
5. Model Identity and Decision Parameters
Which model made this decision? What version? With what settings?
If a bug is traced back to AI-generated code, you might need to understand:
- Was this generated by GPT-4 or Claude Opus?
- What was the temperature setting? (More conservative? More creative?)
- What version of the prompt template was used?
- Were any custom constraints or validators applied?
This information lets you understand whether the bug is a model limitation, a configuration issue, or a genuine governance failure. It also lets you make decisions: "We'll regenerate this code with GPT-4.5, which has better reasoning about database constraints."
Git has no place to store this.
Concrete Failure Modes
Let's walk through three real governance failures that happen because git doesn't capture enough information.
Failure Mode 1: The Invisible Constraint
An AI agent generates a payment validation function. The code looks reasonable: it checks amount, currency, and account status. Three months later, security review discovers the function doesn't validate against a fraud-detection policy that exists in a different service.
A reviewer asks: "Did the AI know about the fraud-detection service?"
No way to know. The commit message says "Implement payment validation." That's it.
You have two options:
- Assume the AI should have known (harsh, because the constraint isn't documented in the repo)
- Assume the AI couldn't have known (letting it off the hook for missing a critical constraint)
Either way, you can't learn from the failure. If the visibility layer recorded "The AI searched the codebase for payment-related code and found no fraud-detection dependency," you'd know the AI tried and missed something. If it said "The AI had no context about fraud-detection policies because they're stored in a different system," you'd know you have a documentation gap.
With git alone, it's a mystery.
Failure Mode 2: The Rebuild Problem
An AI agent generates code for feature X. The code works fine. Eight months later, you have new requirements for feature X, and you ask an AI agent to update the code.
The new AI agent makes different choices because it has different context, different model parameters, or different reasoning. Now you have two implementations of similar functionality, diverging over time.
You'd love to understand why the first AI made its original choices, so you could explain them to the second AI. But git only tells you "what" changed, not "why."
With a reasoning trace, you could say to the second AI: "The original implementation chose strategy A over strategy B because of constraint C. That constraint is still valid. Here's why strategy A was the right call."
Without it, you're hoping the new AI independently arrives at the same conclusion. Often it doesn't.
Failure Mode 3: The Orphaned Decision
An AI agent generates schema migrations. The migrations have a specific structure designed to be compatible with your database replication setup. The decision is embedded in code, but the constraint isn't documented anywhere except in the AI's reasoning.
Six months later, a human developer sees the migrations and thinks they're overly complex. They refactor them for simplicity—not realizing they're breaking the replication compatibility constraint.
The AI's reasoning trace would have said: "Using deferred constraints to maintain replication compatibility with cross-region replicas." That one sentence prevents the mistake.
Without it, the constraint is invisible.
The Commit Message Problem
You might think: "We'll just write better commit messages."
Here's why that doesn't work:
- Humans write commit messages, not the AI: By the time a human writes a commit message, they're writing from memory, or worse, from looking at the diff. They don't have access to the AI's actual reasoning.
- Commit messages are brief: A full reasoning trace—constraints discovered, alternatives evaluated, model identity—can be pages long. You're not fitting that into a commit message.
- Information is lost in translation: The AI's reasoning is precise (e.g., "TTL of 5 minutes minimizes staleness while keeping cache hit rate above 85%"). Translated to a commit message, it becomes vague ("Optimize caching performance").
- No structure: A commit message is unstructured text. You can't query it, search it, or automatically analyze it. A structured reasoning record can be indexed, searched, and used to power governance automation.
- No accountability: Commit messages can be written by anyone, at any time, after the fact. A reasoning trace tied to the AI's actual decision-making is created at the moment of decision and is immutable.
What A Complete Record Looks Like
Here's what you need for AI-generated code:
Decision ID: a1b2c3d-e4f5-6789-0abc
Generated by: Claude Opus 3.5
Timestamp: 2026-02-05 12:34:56 UTC
Git commit: a1b2c3d (user profiles cache implementation)
PROMPT:
Write a function to cache user profile data with Redis.
Use a TTL appropriate for a read-heavy endpoint with infrequent updates.
Optimize for response time. The endpoint serves ~100 QPS.
DECISION SUMMARY:
Implemented Redis cache with 5-minute TTL. Chose Redis over in-process
caching because we have three app servers behind a load balancer, requiring
shared cache state. Chose 5 minutes as TTL because:
1. User profile updates are rare (once per day per user on average)
2. Data freshness SLA is 10 minutes for non-admin users
3. Cache hit rate at 5 minutes is ~87% based on traffic patterns
CONSTRAINTS APPLIED:
- Multi-server consistency: Redis provides shared state across servers
- User table schema: No last_updated field; invalidation requires full scan
- Redis memory limit: Instance has 16GB; profile cache uses max 2GB
- TTL alignment: Auth service caches tokens for 10 minutes; aligning at 5
minutes for slightly more conservative freshness
ALTERNATIVES CONSIDERED & REJECTED:
- In-process LRU cache: Works for single server but breaks with load balancer
- Write-through cache: Would require schema changes to track updates
- Cache-aside with conditional set: Reduces consistency but more complex
without conditional primitives in this Redis client
SYMBOLS TOUCHED:
- cache.py: new CacheManager class
- users/endpoints.py: modified get_user_profile to use cache
- config/redis.py: added cache configuration
- requirements.txt: added redis dependency
CONSTRAINTS THAT WERE NOT SATISFIED:
- None identified during generation
MODEL PARAMETERS:
- Model: Claude Opus 3.5
- Temperature: 0.3 (prioritize correctness over novelty)
- Max tokens: 4096
- Custom validators: [database_schema_check, dependency_audit]
REVIEW NOTES:
Code was reviewed by Sarah Chen on 2026-02-06. Approved.
She noted: "Constraint alignment with auth layer is good; 5-minute TTL
matches our profile update SLAs."This record answers every question a reviewer or auditor might ask:
- What was the AI asked to do?
- How did it reason about the decision?
- What constraints did it discover?
- What alternatives did it consider?
- Why did it choose this implementation?
- Can the constraints change, and if so, what breaks?
Git's Role Evolves
This doesn't mean git is useless for AI code. Git still does what it does well: track changes, manage versions, coordinate merges, provide audit trails for humans. But it needs a companion tool.
Think of it this way:
- Git: Records the commit graph, the diffs, the version history
- Bitloops-style activity tracking: Records the decision-making, the reasoning, the constraints, the alternatives
- Together: You have a complete picture. You can see what changed (git) and why it changed (activity tracking)
Implications for Review and Audit
When you have a complete decision record, code review changes:
Instead of:
Reviewer: "Why is TTL 5 minutes?"
Author: [doesn't exist]
Reviewer: [makes a guess based on the code]You get:
Reviewer: [reads decision record]
"The TTL is 5 minutes because profile updates are infrequent
and this aligns with the user SLA."
Reviewer: "SLA makes sense, but we just changed requirements.
TTL should be 2 minutes now." [overrides the original decision]
[Override recorded for audit: who, when, why, new TTL value]Review becomes effective. Audit becomes possible. Learning from failures becomes systematic rather than guesswork.
Frequently Asked Questions
Can't we reconstruct the reasoning from the code itself?
Partially, if you're very careful. But code shows implementation, not intent. There are a thousand ways to implement "cache user profiles," and you can't tell from the code why this specific way was chosen. You can guess, but you're probably wrong.
Does this mean we need to replace git?
No. Git is essential for version control, merging, and coordination. The point is that git is insufficient for governance. You need both systems: git for change management, and a decision-recording system for AI governance.
Who stores this information?
It depends on your setup. Some teams embed it in commits (but that's painful). Some use a separate audit database (like Bitloops activity tracking). Some write it to structured files in the repo. The mechanism matters less than the fact that it exists and is queryable.
How do we use this in code review?
Reviewers pull up the decision record alongside the diff. They verify:
- Are the constraints still valid?
- Has the reasoning changed?
- Should we override any decisions based on new information?
This makes review faster and more informed.
What about legacy code written by humans?
Human-written code doesn't have decision records because humans didn't create them. That's fine; you only require decision records for AI-generated code (initially). Over time, you might start collecting decision records for human code too—it's just a different format.
Doesn't this create overhead?
Yes, initially. But it prevents the much larger overhead of dealing with production incidents where you have no idea why code was written the way it was. The up-front cost is small compared to the debugging and rework cost you avoid.
How long should we keep these records?
As long as the code exists. If code is deleted, you might archive the records. If code is modified, you keep the original decision record and create a new one for the modifications. This creates a decision lineage: you can see the original intent and how it evolved.
Primary Sources
- Framework for AI governance with requirements beyond traditional version control. NIST AI RMF
- Supply chain security framework extending beyond git with provenance requirements. SLSA Framework
- NIST secure software development framework with decision documentation requirements. NIST SSDF
- SOC 2 governance criteria for audit and decision traceability beyond version control. SOC 2 AICPA
- OWASP security risks requiring traceability beyond commit history. OWASP Top 10 LLM
- OpenSSF scorecard for evaluating supply chain practices beyond version control. OpenSSF Scorecard
More in this hub
Why Git Is Not Enough for AI-Generated Code
2 / 12Also in this hub
Get Started with Bitloops.
Apply what you learn in these hubs to real AI-assisted delivery workflows with shared context, traceable reasoning, and architecture-aware engineering practices.
curl -sSL https://bitloops.com/install.sh | bash