Engineering Maturity Meets Agentic Software Development: Practices That Make AI Coding Safe

11 September 2026
20 min read
Structure
84% of developers now use or plan to use AI tools in their work, so engineering maturity in agentic software development is now a priority.

TL;DR

  • Code generation speed was never the limiting factor. Requirements and architecture account for 70% of a feature cycle, while AI accelerates only the 15% spent on syntax.
  • Agents fail in predictable, structural ways: degraded recall in long files, outdated documentation, unverified external sources, token noise, and conflicting specifications.
  • Context belongs in the repository, not in chat history. Files like DESIGN.md and CLAUDE.md, along with versioned specs, give agents a stable source of truth.
  • Verification needs to happen before a human reviews the output. Compilers, linters, and a testing pyramid lock behavior in place before code reaches review.
  • Modular architecture and strict boundaries limit the scope of any single agent edit.
  • Guardrails, sandboxing, and local CI keep autonomous agents contained and reduce feedback time from minutes to seconds.

According to the Stack Overflow 2025 Developer Survey, 84% of developers now use or plan to use AI tools in their work, up from 76% the year before. DORA’s 2025 report found 90% team adoption. The numbers on trust move in the opposite direction. That same survey found only 29% of developers trust AI output to be accurate, down from 40% in 2024. 

AI tools changed who writes code and how fast. Yet, the factors that determine whether that code is reliable, e.g., clear requirements and rigorous verification, haven’t changed at all. 

In this article, I share insights into software engineering practices for agentic development. I’ll explain how to make AI coding agents reliable, outline the new tech stack, and provide reasons why many AI-based agents fail, as well as how to get the most value from any agent-driven development.

About the Author

Oleksandr Stefanovskyi has a strong background in data science and software engineering and currently leads the Intelliarts cross-functional R&D team. In the Intelliarts blog, he shares his insights on building end-to-end ML pipelines and agentic systems.

His expertise in data science and AI comes from more than 10 years in software engineering, including 4 years leading ML projects. He follows the CRISP-DM methodology in AI and data-heavy projects and holds an AWS certification.

His experience also includes software architecture design and building data lakes and data warehouses for data-intensive applications.

Learn more about AI-assisted SDLC at Intelliarts

Contact us to see how AI-driven development can speed up delivery, cut costs, and improve code quality.

Get in touch
Banner image

Typing Was Never the Bottleneck

Agentic development gets framed as a break from how software has always been built. That framing rests on one assumption: writing code was the constraint holding teams back. The data on where a feature cycle actually spends its time says otherwise.

The myth of the “AI-disrupted” SDLC

In a recent webinar on engineering practices for agentic development, I broke down where a typical software lifecycle actually spends its time:

  • Requirements & Intent — 40% of cycle time
  • Architecture & Boundaries — 30%
  • Testing & Verification — 15%
  • Syntax (Coding) — 15%

The big idea here is that the 15% slice is the only piece that generative AI meaningfully accelerates. The other 85%, the reasoning about what to build, how to structure it, and whether it solves the right problem, stays entirely human work.

This reframes what “10x productivity” claims usually mean in practice. Speeding up 15% of a cycle, even by a large factor, still leaves the other 85% untouched. When requirements and architecture already eat most of a sprint, that’s where the real leverage sits.

“Measure your team’s cycle time, then target the architectural-design bottleneck.”

The new role: humans orchestrating agents

If typing was never the bottleneck, the job of a senior engineer was never about typing either. What’s shifted is how visible that fact has become. 

The hardest work has always been reasoning around a feature, and deciding whether it should exist at all. LLMs automate typing; they cannot determine intent.

  • The idea behind an autonomous individual directing agents comes down to this: one person now holds the authority that used to sit with an entire team. That person doesn’t write most of the code anymore. They decide what an agent should attempt, review what it produces, and pull the plug when it drifts off course.

Two very different modes of contribution now sit inside a single workflow:

  • Syntax generation. Sub-second, high-volume, prone to local drift, incapable of verifying whether the output matches what was actually meant.
  • Reasoning & intent. Defines what gets built and whether it should be built, enforces the invariants that hold a system together, and remains “the primary human responsibility”.

The agent can implement a function in seconds. Yet, it cannot tell you whether that function belongs in the codebase, whether it breaks an assumption three files away, or whether the user even asked for it. Someone still has to catch that, and increasingly, it’s one engineer instead of a chain of specialists.

Multi-role engineer diagram with five supporting roles

The multi-role engineer diagram puts it visually: one engineer, plus agents, sits at the center, connected to a Product Manager, an Architect/Tech Lead, a QA Engineer, and an Engineering Manager, four roles orbiting one person instead of four separate desks.

This matches how we actually hire at Intelliarts. Our senior technical interviews weight design and product reasoning far above raw syntax recall:

  • Design Session — 35%, evaluating how a candidate partitions systems and separates concerns
  • Product Session — 30%, focused on business constraints and trade-offs
  • Engineering Values — 20%, covering ownership, quality bar, and pragmatism
  • Technical Code Skills — 15%, the part AI now accelerates

“Spend the first 10 minutes of every feature cycle acting explicitly as a Product Manager, slice the requirement, before you invoke an agent.”

The new toolset landscape (Cursor, Claude Code & Co.)

Agentic development tools, as of 2026, can be split into two main categories. IDE-native agents that operate inside the editor and CLI-native agents that run from the command line with broader system access. 

The level of autonomy of each category is different, so let’s find out which one can serve a particular project better:

IDE-native vs CLI-native agents

Cursor and Claude Code take different approaches to how an agent interacts with a codebase:

Cursor IDE agent vs Claude Code CLI agent comparison

  • Cursor keeps a developer in the loop for each suggestion, which suits tasks where visual context matters and scope stays narrow. 
  • Claude Code operates with less supervision, executing multi-step tasks across many files before reporting back.

“Task complexity determines the tool, because a developer can only verify so much in real time. Cursor suits changes reviewable inline, one file at a time, while Claude Code is for scenarios where changes should be verified post-factum, across many files at once.”

A component refactor with tight visual dependencies favors an IDE-native agent that keeps a developer watching each change. A batch migration against a defined contract favors a CLI-native agent that can run unattended and report results.

Different UX, same need for engineering maturity

Cursor keeps a developer inside the editor for every step, showing suggestions inline before they land. Claude Code removes that visibility. A developer issues an instruction from the terminal, and the agent works through multiple files and test runs on its own, reporting back only once it’s done, with review happening against a finished diff rather than a step-by-step approval screen.

This is why I think of this split as moving from “ad-hoc chat prompting to systematic repository configuration”. 

The UX gap is there, but it doesn’t change what makes either tool safe to run. An agent with more autonomy just needs more of the safety net built in advance, since nothing catches a mistake mid-task the way Cursor’s interface does.

Safety concerns are the same regardless of which tool exposes the gap:

  • No lint rules or type checks. An agent ships code that compiles but violates conventions nobody enforced, in an editor sidebar or a terminal window alike.
  • No module boundaries. An agent reaches across a codebase and touches files outside its intended scope, since it has no way to infer architectural lines that a human hasn’t defined.
  • No repository-level context files. Without an AGENTS.md or equivalent, the agent reconstructs its understanding of the project from scratch each session.

The fix is the same across both tools: context design, verification loops, and guardrails, each covered in detail later in this piece. Claude Code, with less built-in supervision than Cursor, simply needs that safety net in place before the work starts.

  • Important note. The choice between agents boils down primarily to the repository configuration, with prospects of having a stack that is safe to run.

Want to know the real cost of AI adoption for your company?

Learn what drives the cost of LLMs in enterprise environments, including real-world benchmarks.

Download white paper
Banner image

The real bottleneck: Thinking, not syntax

So, if requirements and architecture eat 70% of a cycle, the people who handle that work carry the actual weight of a project, and their value is never in how fast they can type.

Why senior engineers are hired for partitioning, not recall

Partitioning a system, deciding where one module ends and another begins, has always separated a senior engineer from a junior one. 

  • Big idea: An agent can produce syntax at any skill level, so syntax stopped working as a filter for seniority.

That’s how Intelliarts weights our senior technical interviews:

  • Design Session — 35%, evaluating how a candidate partitions systems and separates concerns
  • Product Session — 30%, focused on business constraints and trade-offs
  • Engineering Values — 20%, covering ownership, quality bar, and pragmatism
  • Technical Code Skills — 15%, the part AI now accelerates

“We do not assess seniors on syntax recall. The Design and Product sessions evaluate how a candidate partitions systems, separates concerns, and reasons about trade-offs.”

Recall is used to correlate with experience, since a developer who had solved a problem before could reproduce the solution faster. An agent retrieves any syntax pattern instantly, regardless of who’s asking, so that correlation no longer holds. 

Judging whether a given partition holds up under load, or whether a boundary drawn today will trap the team in six months, still falls to a person. That judgment is what the interview weighting is built to catch.

Cheap code, expensive review: the democratization paradox

Lowering the cost of writing code shifts the cost downstream, toward review and architectural oversight. I name this shift directly: “cheaper code raises the bar.”

The mechanism is straightforward. When code generation becomes frictionless, the volume of code produced increases. An agent optimizes for a task passing its immediate check, not for how the code fits the system around it, so a large share of that volume ends up poorly structured. The demand for architectural review and supervision grows in proportion to how much code gets generated.

“Zero tolerance for undocumented agent modules; every agent PR must include human-reviewed architectural justification.”

That zero-tolerance stance is the practical response to the paradox. A team that lets agent-generated code merge without architectural review treats faster output as the win, when the bottleneck has already moved to the review stage. Senior engineers spend more time reading diffs because code has become cheap to produce.

Why agents fail: Five context pathologies

AI coding agents are deterministic token-parsing engines, so their failures follow predictable patterns rather than random mistakes. I group these patterns into two families: context pathologies and interface deficits. 

The first family covers five distinct ways an agent’s context window can work against it, each with its own root cause, symptom, and fix.

PathologyRoot CauseToken Failure ModeSymptomImmediate Action
1. Lost in the MiddleFiles >300 lines / context >100k tokensRecall drop-off in the middle of long sequencesHallucinations; ignored requirementsEnforce a 300-line limit; split modules
2. Context RotStale specs & legacy docs in scan pathRetrieves legacy contracts that conflict with codeZombie loops on deprecated APIsArchive to docs/archive/ and ignore it
3. Context PoisoningUnverified tutorials / SO snippetsInjects untrusted external logic into promptVulnerable auth; dependency creepRoot AGENTS.md of approved libs & rules
4. Context Noisedist/, logs, coverage, node_modulesScans burn ~80% of token budget on noiseAttention dilution; slow, costly runsConfigure .cursorignore / .claudeignore
5. Context ConflictContradictory rules across active filesModel receives opposing constraintsInfinite loops; agent undoes its editsOne schema source of truth; delete dupes

Before breaking down each one individually, the five pathologies share a common structure worth noting upfront:

  1. Each starts with a specific, identifiable root cause: a file that’s too long, a document that’s gone stale, a source that was never verified
  2. Each produces a symptom that looks like the agent being careless, when the actual cause is structural
  3. Each has a single, concrete fix that removes the root cause rather than patching the symptom

Lost in the middle

“LLMs recall the start and end of a context window strongly, but retrieval collapses in the middle. Long contexts (100k+ tokens) degrade reasoning and invite hallucinations.”

Recall accuracy holds near 95% at the start and end of a context window, then drops to roughly 20% in the middle. A requirement has a real chance of getting ignored because the agent’s attention degrades with distance from the edges. 

The fix suggested by our team: Keep files under roughly 300 lines, so nothing important ever lands in the collapsed middle of a long sequence.

Context rot (dead docs)

Outdated specifications rarely get deleted from a repository. They stay in the indexed path next to current code, and an agent has no reliable way to distinguish a contract that shipped last week from one abandoned eight months ago. Every document in the scan path carries equal weight by default, so a stale README can override a schema file it directly contradicts.

To explore how to deal with context rot through AI-based technologies, read our blog post on how RAG systems improve LLM.

A few practical markers separate live context from dead context:

  • Deprecated API specs still sitting in the scan path
  • Old design proposals that contradict shipped code
  • Documentation with no recent commit activity is a signal that the agent has no way to check on its own

The remedy is a docs/archive/ folder outside the agent’s scan path, with outdated specs moved there immediately, and the folder added to ignore files.

Context poisoning (unverified external data)

“Feeding the agent unverified external docs, outdated tutorials, or buggy StackOverflow snippets injects security holes, deprecated auth patterns, and dependency creep.”

That’s how this poisoning enters an agent’s workflow in three steps:

  1. A poisoned source gets fetched: an outdated tutorial, a buggy forum snippet, a deprecated library reference.
  2. The agent writes the deprecated or vulnerable pattern directly into the codebase.
  3. Even when a sandbox catches and blocks the resulting code, the poisoned reference stays in the agent’s context for the rest of the session.

That third step is the part teams tend to miss. Blocking a single bad output doesn’t clear the contaminated context behind it. Typo-squatted packages, vulnerable snippets, and hallucinated dependencies can all resurface later in the same session, even after the original suggestion got rejected.

The fix is a root AGENTS.md file listing approved, pinned dependencies. That gives the agent a trusted reference to check against, instead of trusting whatever it happens to fetch from an unverified source. Teams building LLM-powered applications face this same risk at the model layer.

Context noise (logs and build spam)

“Scanning build outputs, logs, local databases, and node_modules can burn ~80% of your token budget on non-functional noise, diluting attention.”

A typical repository splits into two zones: source directories like /src, /tests, and /docs that carry the actual signal, and generated or transient directories like /dist and /node_modules that carry none. 

The second category can easily outweigh the first by volume: a node_modules folder alone commonly holds thousands of dependencies and hundreds of megabytes, none of it relevant to the task an agent was given.

Basically, my point here is that the 80% figure changes how a team should think about ignore files. An agent configured with a .claudecodeignore or .cursorignore works from a clean, high-signal view of the codebase. Without one, it scans through gigabytes of transpiled bundles and dependency trees to locate the handful of files that actually matter.

Context conflict (contradictions and loops)

The most disruptive pathology happens when two trusted sources disagree. An active TypeScript interface defines a field as a string, while a stale architecture document, last edited eight months earlier, defines the same field as a number. The agent tries to reconcile both, produces a hybrid type, breaks compilation, and retries.

An agent stuck in this loop doesn’t fail loudly. It keeps attempting fixes, each one wrong in a slightly different way, until something interrupts the cycle. Common triggers behind this pattern include:

  • A stale README describing a schema that code has since outgrown
  • Two ADRs written months apart, neither one marked as superseding the other
  • A type definition duplicated in both code and markdown, with the two falling out of sync

From what we at Intelliarts observed, the solution in a nutshell is to maintain one source of truth for schemas and delete any markdown that duplicates a type definition already present in code.

Taken together, these five pathologies point to the same lesson: an agent fails because its context window sometimes contains something not actually intended, resulting in poor output as per the Garbage-In-Garbage-Out (GIGO) rule. 

Designing context the machine can actually use

A repository structured so that an agent can find the right context without guessing resolves the five pathologies above. That structure belongs inside the codebase.

Source code vs hidden system intent iceberg diagram

Bringing “why” into the repo

“Raw code explains how a feature works, but never the business rules, the users, or the rationale that governs the system. Those layers sit above the syntax.” 

Source code represents the visible fraction of what a working system depends on. Developer intent, business constraints, architectural conventions, and a team’s unwritten conventions sit beneath that layer, inaccessible to an agent reading a function body.

At the same time, code communicates how something works to an agent. From my perspective, the rationale behind a design decision, and the business rule it enforces, remains undocumented unless a team records it explicitly. A concise DESIGN.md, covering user personas, domain boundaries, and constraints a change must not violate, addresses that gap.

The same context-design discipline governs retrieval-augmented generation systems. Take a look at a corresponding blog post by Intelliarts for more information. 

From chat prompts to versioned specs

Instructions entered into a chat interface disappear once the session closes, along with any record for the next developer or the next agent run.

“Long instructions typed into a chat vanish when the session closes. Design specs, ADRs, and rules belong inside the repository, where they evolve with the code.”

Moving that instruction into a versioned file changes its properties measurably:

  • Retention shifts from ephemeral to permanent, evolving alongside the codebase
  • Traceability shifts from none to full visibility in commit logs
  • Consistency shifts from highly variable results to deterministic, repeatable output

A CLAUDE.md file in the project root, documenting setup, run commands, and style conventions, establishes a consistent starting point for every session, reducing the need to re-explain project context repeatedly.

Contracts that survive agent runs

A specification committed alongside the code it describes remains accessible to a CLI agent. A specification stored only in institutional memory or an external wiki stays out of reach.

This creates a strict connection: every commit carries both the code change and the spec or ADR that justifies it, giving a reviewer, or an agent initiating a new session, direct access to the reasoning behind the change.

The same applies to API contracts. An agent can alter a JSON payload or database schema in ways that break downstream consumers, and a passing test suite may not detect it. 

Static schema diffing addresses this: an automated tool compares a proposed change against the base contract, rejects changes that introduce breaking modifications, and allows compatible changes to proceed to review.

“An agent can silently alter a JSON payload or database schema and break every frontend consumer. Static schema diffs compare generated output against the base contract.”

Running that diff as part of a pre-commit hook prevents an undocumented, breaking API change from reaching a branch.

Verification: Feedback loops agents self-correct against

Structuring context keeps an agent working from correct information. Confirming its output is correct comes from a separate layer: feedback loops the agent checks itself against, before a human ever reviews the diff.

Compiler and type system as ground truth

A compiler enforces its rules without exception, which makes it a natural first gate for agent output.

“The compiler and type checker are an absolute, non-negotiable feedback channel; the agent self-corrects against them before a human ever looks.”

Compiler error to agent fix loop diagram

Here’s the mechanism: a compiler error identifies the exact file, line, and column where a type violates its contract. An extractor tool turns that location into a targeted prompt. The agent applies a scoped fix until the code recompiles cleanly.

This loop resolves compiler-detectable errors without human involvement. Catching a broken type by hand costs a reviewer minutes per instance. An agent checking its output against tsc –noEmit catches the same error in seconds, clearing type mismatches from the diff before a reviewer opens it.

Linters, AST rules, and auto-formatting

A compiler is intended to detect broken types. A circular import, a private module exposed across a boundary, or an unenforced style convention needs a different layer of static analysis.

There is a three-gate sequence an agent’s edit passes through before it’s considered safe (Slide 21):

  1. AST parser. A structural pre-check that catches architectural violations, like a deep import reaching into another module’s internals
  2. tsc –noEmit. Type safety enforcement, the same compiler gate described above
  3. ESLint. Style and anti-pattern checks that keep the codebase consistent regardless of which developer, or which agent, wrote a given file

AST parser, type checker, and ESLint gate sequence

Based on what we observed in our projects, formatting plays a smaller but measurable role in this same pipeline. I describe Prettier as a token strategy: unformatted code produces high diff noise from tabs, braces, and spacing changes unrelated to logic, which wastes tokens and slows review. Strict formatting strips that noise, so a reviewer or an agent reading a diff spends attention on the change that matters.

The testing ladder of confidence

Static checks confirm code compiles and follows conventions. Confirming the code does what it’s supposed to do is the job of tests, and the presentation frames them as a pyramid of increasingly broad, increasingly expensive guarantees, the same discipline our software engineering services apply to every project. 

  • Unit tests. Secure local invariants and edge cases, fast and cheap to run
  • Component API tests. Guard module contracts and boundaries
  • Integration tests. Catch secondary side effects that isolated tests miss
  • E2E tests. Validate a complete user workflow end to end

Unit, component, integration, and E2E testing pyramid

Unit tests carry the most weight for agent-written code specifically, because they lock behavior against regression. The mechanism is simple: a test file sits in a boundary that an agent is not permitted to edit. Only the implementation file stays open for changes. A failing test defines the requirement in concrete terms, and the agent iterates against that fixed target until the suite passes.

“Never tell an agent ‘it’s broken.’ Write a unit test that fails under the broken condition, and let the agent fix it green.”

Telling an agent a feature is broken invites a guess at what “broken” means. A failing test defines success in concrete terms, leaving no room for the agent to misinterpret it.

Component API tests extend that locking mechanism to module boundaries. Integration and E2E tests extend it further still. Each layer catches what the one below it structurally cannot see:

  • An ESLint rule blocks a deep import past a module’s public gateway. Every consumer goes through one versioned interface instead.
  • A user workflow, sign-in through checkout to confirmation, reveals side effects once the full sequence runs together.

Running that full sequence with a fast, headless E2E runner after a major agent edit confirms the whole workflow still holds, not just the pieces tested in isolation.

Architecture that gives agents room to work

Verification catches bad output after the fact. The structure of a codebase determines how much bad output an agent can produce in the first place, and that structure comes from architecture decisions made before any agent touches the repository.

Small modules, clear boundaries

A codebase that’s hard for a human to navigate produces worse results for an agent. A tightly-coupled system, where a single change ripples across a dozen files with unclear dependencies, gives an agent no reliable way to scope its own edit.

“If a human struggles to navigate a tightly-coupled codebase, an agent fails spectacularly. Decoupled modules cut the number of files an agent must read to make a change.”

Tightly-coupled vs modular architecture comparison

Compare two outcomes:

  • A tightly-coupled repository with six modules cross-referencing each other leaves an agent failing within two minutes. 
  • The same task in a modular repository, split into clean domain, api, and infra folders with their own tests and docs, ships in five.

Small modules extend that same principle to the class level. Let’s set a concrete ceiling: 150 lines per class, 25 lines per function. Enforcing that ceiling keeps a change locked inside a single module. A class or function within those limits rarely accumulates the kind of hidden dependencies that spread a change across a repository.

  • A focused edit stays contained to the file it touches
  • A module beyond scope stays explicitly blocked, with an instruction like “refactor orders, do not touch files outside src/orders/”
  • The blast radius of a mistake shrinks to whatever that single module contains

Looking for external expertise on agentic development and data handling in software?

See our expertise
Banner image

Designing “safe zones” for autonomous changes

Some parts of a codebase tolerate an agent working with minimal supervision. Others don’t, and mixing the two without a clear boundary invites unscoped edits.

A safe zone, in practice, is a directory an agent has explicit permission to modify, paired with directories it’s explicitly blocked from touching. I define an active scope: src/orders/ marked open for edits, while src/payments/, src/users/, src/notifications/, and src/shared/ stay locked for the same task.

That boundary sets a hard limit on what a mistake can cost:

  • Without a boundary, an agent fixing an unrelated bug can drift into payment logic.
  • With a boundary, the same agent has no path to touch anything outside its assigned directory, regardless of how far a flawed fix drifts.

That restriction pairs naturally with the module boundaries from the previous section. A codebase already split into domain, api, and infra gives a team a ready-made set of safe zones. Each folder already represents a self-contained unit an agent can work inside without touching the others.

Git and review discipline in the agentic era

Architecture defines the scope of an agent’s access. Git discipline governs the pace and reversibility of its changes, determining how quickly a team can identify and undo a mistake once one occurs.

Commit hygiene for humans and agents

“Letting an agent touch 20 files over two hours and then committing once is an anti-pattern. Commit early and often; every commit is an instant rollback checkpoint when the agent loops.”

Commit rollback and branch advance workflow

Here I outline a hook-based implementation of this principle: a post-write hook runs the unit test suite after every commit. A passing run advances the branch. A failing run triggers an automatic git reset –hard HEAD~1, restoring the workspace and prompting the agent to retry with the failing-test context intact.

This cycle establishes a checkpoint at every commit, limiting the scope of any single rollback:

  • A commit after each passing test run keeps the rollback distance minimal
  • A blocked git push –force at the pre-command hook prevents an agent from overwriting protected history

Squashing and history that’s still readable

Frequent commits address the rollback problem, but introduce a separate one: a main branch accumulating dozens of “wip” commits that carry little meaning for anyone reviewing the history later.

We address this with a standard pattern: an agent’s iterations remain on a feature branch, often six or more small commits deep, and a single squash merge consolidates them into one clean entry on main.

“Dozens of micro-commits from agent iterations clutter the history. Squash on merge: the feature branch keeps the detailed steps, while main stays clean and auditable.”

The detailed record remains available on the feature branch for anyone tracing an agent’s iterations. The main branch reflects the outcome, which serves most readers of the history. Branch-protection rules requiring squash-and-merge on every pull request enforce this as a default rather than a manual step.

Human-underwritten PRs

An agent generates code faster than a human can review it. We can quantify that gap: an agent produces 1,000 lines in two minutes, while a human requires hours to audit the same diff for safety.

Review quality vs pull request size chart

Review quality holds near 90% up to roughly 200 lines, then declines sharply beyond that threshold, according to the chart. A reviewer examining a 1,000-line diff identifies a fraction of the issues the same reviewer would catch in a 200-line diff.

Our engineering practice shows that capping PR size at 200 lines keeps a diff within the range where review quality holds. Large features still ship, structured as a sequence of smaller, independently reviewable branches rather than a single diff too large to audit properly. A pre-push hook enforces the cap by rejecting any push that exceeds the line budget.

“A lot of failures we’ve reviewed came from an undefined standard in coding and QA. That’s basically a variation of any scenario describing communication and project scope errors.”

Guardrails for autonomous agents

Architecture and Git discipline shape how an agent’s code moves through a repository. Neither one restricts what an agent can execute directly on a system, and a CLI agent with shell access can run commands, write files, and index a filesystem well beyond the scope of a single task.

Permissions, ignore lists, and lifecycle hooks

“Autonomous CLI agents can run shell commands, write files, and index the system. Three layers of guardrails contain them: environment isolation, active ignoring, and lifecycle hooks.”

You can structure those layers as concentric rings around agent execution:

  • A lint check forms the outer ring, catching static safety issues
  • A dangerous-command checker sits inside that, matching patterns like curl, wget, or rm -rf before they run
  • A filesystem boundary enforces scope constraints
  • A compliance auditor at the center logs every action the agent takes

Ignore files handle a related but distinct problem: defining what an agent is permitted to see in the first place. Filtering node_modules/, dist/, log files, .env, and .git/ out of an agent’s context reduces the visible codebase to roughly 20% of the raw repository, all of it relevant source.

Lifecycle hooks tie both mechanisms to specific moments in an agent’s workflow. I break the sequence into three stages:

  1. Pre-command hook. Audits and validates a proposed action, blocking commands like rm -rf / or git push –force before execution
  2. Agent action. The actual write, run, or edit
  3. Post-command hook. Verifies and formats the result, running Prettier, a typecheck, and a security scan like trivy fs .

Sandboxed execution and pre-commit gates

Permissions and ignore lists constrain what an agent can see and attempt. A sandbox constrains what an agent’s mistakes can reach, even if a guardrail fails to catch one.

I draw that boundary directly: a Docker container mounts only a clone of the repository, with no access to host credentials, SSH keys, or other projects on the machine. An agent running inside that container has full toolset access, shell, write, index, but no path to exfiltrate a credential or touch a file outside the mounted clone.

Pre-commit gates add a second layer of enforcement, catching what a sandbox alone doesn’t verify: whether the code an agent produced actually meets the repository’s baseline standards.

“No code reaches the branch unless it meets basic repository invariants. The hook runs linting, typechecking, and core unit tests, and blocks the commit on any failure.”

A commit attempt runs through three checks before it’s accepted:

  • Lint, catching style and anti-pattern violations
  • Typecheck, catching broken contracts between modules
  • Core unit tests, catching regressions in existing behavior

A failure at any stage blocks the commit and returns the agent to fix and retry, rather than letting broken code reach a branch where a human has to catch it manually.

Local CI mindset 

Guardrails and pre-commit gates catch bad code before it merges. The speed at which an agent learns its code failed determines whether that check accelerates work or introduces friction.

Remote CI vs local CI latency chart

That latency gap produces two distinct outcomes:

  • A remote pipeline failure surfaces after the agent has already moved to a different task, requiring a context switch to diagnose the issue
  • A local pipeline failure surfaces within seconds of the edit, while the agent retains the relevant context, enabling immediate correction

The resulting workflow: an agent runs the local script immediately after an edit, resolves any failures, and pushes only code that clears the full local suite. 

Big idea: Because of the 10 to 30-second local check, verification is handled inside the agent’s own workflow. Therefore, it’s checked and corrected before a human or a remote pipeline ever sees the result. 

So, this brings us to an insight and an action: local CI resolves mechanical errors before a human ever opens the diff, leaving one question for a reviewer to answer: whether the change belongs in the codebase at all. 

Conclusion 

Reducing the search space, enforcing boundaries, and verifying against a compiler or test suite: these are the same practices covered across this piece, from context design and architecture to guardrails and local CI. None of them are new to software engineering. 

What changed is the volume of code moving through them and the speed at which it arrives. Writing code stopped being a scarce skill once an agent could produce it on demand. 

Directing that agent, setting its boundaries, and verifying what it built remains the work only a person, or a technology consulting partner, can do. 

Where to go next

The practices covered here come from my personal experiences. Should you have any concerns left or require any consulting or development assistance, proceed with a trusted AI development partner.

The Intelliarts team has been on the market for more than 26 years, providing AI/ML and software development services to businesses worldwide. With a 90% customer return rate and a focus on business objectives, we can offer assistance with projects of any complexity.

Looking for AI agent development and implementation?

The Intelliarts team specialises in agentic solutions that drive business efficiency

Contact us
Banner image
Oleksandr Stefanovskyi
AI Solution Architect
Rate this article
0.0/5
0 ratings
Structure
The Economics of LLM Adoption
The Economics of LLM Adoption
White Paper
Get your copy
Related Posts