INNOVATION
25/03/2026 • Stijn Huygh

Architecting for AI: how to prevent runaway code

We need to talk about the "illusion of speed” in the age of AI.

In times where every post on LinkedIn seems to show that all your competitors are making a shift from traditional coding to 100% AI agentic software development, reality is somewhat more complex. Although AI demos and PoCs can give impressive first results, embedding AI Agentic software development in a long-term application lifecycle, with production-grade requirements, is still a different ballgame.

At ACA, we start from defining and measuring our quality standards and objectives, structures, controls and guardrails. Then, as we add AI agentic capabilities we can verify and control outcomes in a factual and continuously monitored way. If our AI setup helps us to speed up within those boundaries it’s great. But if AI hits our guardrails, we immediately detect it and we pull back.

As a result, today AI agentic software development gives us efficiency gains of 20 to 50% compared to traditional coding. All while maintaining our quality standards in terms of performance, security and maintainability.

In this article by Stijn Huygh, we take a deeper dive in how we realize this.

Why fast-looking AI output can quietly undermine software quality

Every developer has experienced the initial rush of working with a coding LLM. You ask for a feature, and seconds later, you have code. It compiles, it runs, and it looks mostly correct. 

But speed can be deceptive.

The biggest risk of AI-assisted development is not that the code will fail. It is that it will work while quietly degrading your architecture. Without guardrails, AI does not follow your engineering culture. It follows the statistical average of the internet. That is rarely what you want in a production-grade system.

If you do not control the system, you do not control the output.

Therefore, it is important to engineer a software development lifecycle that focuses on maintaining the integrity of your code base. You want the end result to be performant, secure and definitely maintainable.

The real problem: AI-induced technical debt

AI does not understand your architecture, it reads code, it predicts patterns and outputs what is statistically the most likely answer.

This becomes a problem when your system relies on intentional structure. The model does not know your architectural decisions or the reasoning behind them. It just tries its best to generate working code within the instructions it has.

The result is code that technically works, but slowly drifts away from the architecture you designed.

That drift often shows up in subtle ways:

  • unused methods
  • hard-coded strings
  • Importing code across logical boundaries 
  • DTOs leaking into the UI
  • logic that compiles but violates your rules

None of these issues immediately break the application. The code runs and the feature works, but over time, these small deviations accumulate. Technical debt introduced by AI can grow faster than human code review can realistically catch. And that is where the real problem begins.

So we changed the approach. Instead of chatting with AI, we started engineering the constraints around it.

The framework for high-integrity AI engineering

1. Enforce architecture physically, not just in documentation

The very first step to improve the quality of the code base, even without using AI to generate code, is determining how you want your project to be structured. The goal is to enforce consistency within your project, but also over all your other projects. When a developer opens the codebase they should immediately be able to know where everything is, based on your conventions. Determine your application architecture!

The most common mistake teams make is relying on PDF documentation or a wiki page to enforce architecture. AI (and humans) will forget there is a standard and just ignore it. It will become a lot easier to maintain a high quality codebase when your architecture is also enforced in code and not only in documentation.

One of the ways we enforce this, is by separating our layered architecture in different projects/packages. If code is not exposed to another layer, or the layer you are in does not have access to a specific package it will not be used by that layer.

Think of folders like painted lines on a road; they are suggestions that are easy to drive over by accident. Separate Projects (or Packages) are concrete walls. By structuring your layers as distinct packages with pubspec.yaml rules, you physically prevent the AI from importing code from the wrong layer.

If the AI tries to import the API layer directly into the UI, the compiler (not a human reviewer) blocks it immediately.

Our layered architecture looks like this:

  • _p4_ui: Purely presentational. No business logic allowed, MVVM pattern.
  • _p3_application: Orchestrates use cases. Never throws exceptions. Returns Response objects.
  • _p2_data: Handles DTOs. Rule: DTOs never leave this layer; they are mapped immediately to the domain. DTO’s are therefore never leaked in other layers.
  • _p1_domain: Pure business logic.
  • _p0_*: Shared foundations such as DI, translations, utilities and design system.

If something violates the architecture, the compiler flags it. This removes ambiguity and reduces discussions during code review.

2. Define strict coding standards and enforce them

Your application architecture should also be enriched with coding standards to further improve quality and consistency. Some examples of code standards we enforce:

Naming

  • Interfaces start with I
  • Services end with Service
  • DTOs end with Dto
  • ENums start with E

Complexity limits

  • Max 3 nested if statements
  • Max 75 lines per file
  • Max 500 lines per file

Completeness

  • All switch cases handled
  • No use of dynamic

Application layer rule

  • No exceptions. Only Response objects.

Ordering

  • Properties, fields, constructor, public and private functions are always in specific order in your class. 

Most of these rules are enforced through linters and static analysis. This is not about strictness for the sake of it. But it is about predictability. When structure is predictable, deviations stand out immediately.

3. Use deterministic scaffolding

One lesson we learned quickly: never let an LLM generate your boilerplate. It gets "creative" with file names and folder structures, leading to a messy project tree.

We use "dumb tools" for structure so the "smart tools" (AI) can focus on logic. Tools like Mason allow you to generate deterministic templates using the mustache template system. A command like “mason make viewmodel” creates the perfect folder structure, filenames, and class definitions in mere seconds. The view, viewmodel, interfaces and services are all created with exactly our project structure and coding guidelines in mind. 

The strategy: when you integrate AI, do not ask it to "write the file." Instead, give the AI access to your CLI via the Model Context Protocol (MCP) or directly via terminal. The AI becomes the operator of your scaffolding tools, executing the mason command to ensure 100% consistency before it writes a single line of logic.

4. Treat warnings as real issues

AI-generated code often compiles, but it may still introduce warnings such as unused variables, implicit casts or minor performance issues. On their own, these seem harmless. Over time, however, if those warnings are ignored, they accumulate and gradually reduce the overall quality and maintainability of the codebase.

You should treat AI code with the same suspicion as human code. We apply a zero-warnings policy in our CI/CD pipeline. If warnings are present, the build fails.

On top of that:

  • Static analyses must pass
  • Naming rules are validated
  • Unit tests must pass before merging 

Tests are especially important. AI is very good at refactoring. It is less good at understanding business impact.

Automated checks help us keep confidence high.

5. Structure documentation for AI: the 3-tier documentation system

"Generic training data produces generic code." If you want the AI to write code that looks like your team wrote it, you must feed it your context.

We use a 3-tier documentation system to feed the context window efficiently.

Tier 1: golden rules (always on)

A lightweight file with non-negotiables. For example:

  • The Application layer never throws exceptions.
  • DTOs never leave the Data layer.
  • New views are generated through the approved scaffolding tools.

Tier 2: Architecture deep dive

Reference documents that explain:

  • Layer responsibilities
  • Testing patterns
  • Design system usage
  • Coding guidelines

Tier 3: Workflows and agent instructions 

Step-by-step instructions for how AI agents should operate.

Clear documentation reduces ambiguity. And reduced ambiguity leads to more consistent output.

6. The Agentic Workflow: split the brain

The biggest leap in quality comes from moving away from a single chat window to specialized AI Agents. This separation of concerns prevents hallucinations by limiting the scope of each agent.

Instead of one "Coder," we deploy a team:

1. The architect (the thinker)

  • Constraint: NO CODING.
  • Role: Reads the user story, explores the codebase, and writes a plan.md. This is a step-by-step execution plan (e.g., "1. Create DTO, 2. Map to Domain").
  • Benefit: “Human in the loop“ - You review the plan before the code is written, preventing over-engineering.

2. The coder (the do’er)

  • Constraint: Works one step at a time.
  • Role: Reads plan.md, implements Step 1, runs tests, updates the plan, and STOPS for approval. It cannot skip ahead.

3. The reviewer (the gatekeeper)

  • Constraint: Max 15 comments, prioritized by severity.
  • Role: Specialized in spotting tier-violation and copy-paste errors that humans often miss.

From writing code to designing systems

High-integrity AI engineering isn't about writing better prompts, but about building better systems.

When AI repeatedly makes the same mistake, the solution is not a better prompt. It is a better system. 

  • Strengthen architectural boundaries
  • Add or refine linter rules
  • Improve scaffolding
  • Clarify documentation

Over time, this creates an environment where AI becomes a reliable accelerator instead of a source of noise.

Key takeaways

  • AI accelerates development. It can also accelerate technical debt.
  • Architecture should be enforced in code, not only described in documentation.
  • Deterministic scaffolding improves consistency.
  • Automated quality checks protect long-term maintainability.
  • Clear documentation and structured workflows improve AI output.

AI is powerful. But it performs best inside well-defined systems.

Structure does not slow you down. It allows you to move fast with confidence.

Looking to scale AI development without compromising quality?