Layered Architecture Enforcement

Discussion of strict dependency rules between code layers, ports and adapters patterns, mechanical enforcement of architectural constraints

← Back to Harness engineering: Leveraging Codex in an agent-first world

Commenters argue that strict layered architecture, once reserved for massive engineering teams, has become an essential prerequisite for managing the high-velocity output of AI coding agents. By implementing "harnesses"—ecosystems of deterministic tools, Gherkin features, and pre-push hooks—developers can mechanically enforce dependency rules that prevent LLMs from producing "spaghetti" code or architectural drift. Many participants suggest that because AI models generate code one token at a time, these rigid guardrails are necessary to ensure system integrity and allow for rapid development without structural decay. Ultimately, this approach shifts the developer's role from writing manual logic to building a robust environment where agents can autonomously navigate complex layers and adhere to contract-first development patterns.

9 comments tagged with this topic

View on HN · Topics
I've been doing the same experiment in tsz[1] for a while now (the same past five months in fact) and I have come to very similar conclusions. Lots of harness to enforce good architecture splits. Lots of tests and CI. My point of working on tsz is to learn how to do very big projects with AI. Eventually the same workflows and attitude can be leveraged to build customer product apps with UI as well. I see that OpenAI is leveraging automated browser testing and even videos as part of their workflow. I think as models get better this direction for making software would eventually make sense. I don't think we're there yet though. But at least, unlike OpenAI vague claims I can share the output with you to see! Most of the solutions that offer a very high level of automation like Lovable are a bit too optimistic and solutions are not tightly coupled with lots of automated testing. [1] https://github.com/tsz-org/tsz
View on HN · Topics
I do quite a lot of what this post describes in a reasonably large project. Here's what works for me: - write gherkin features for new features; update them for enhancements; don't touch them for refactors. Label your PRs with these nouns. - use pre-push hooks for type checks, linting, unit tests, and other quick, scriptable validations. - make a viteperess subsite in your repo, have the agents maintain it - document important principles, architecture, etc. - make a cli command which lists all pages along with the yaml frontmatter description so agents can choose what to read without blowing up the context window. - use ddd and monorepo - write your logic in headless layers, and compose layers into apps. agents navigate layers very successfully. - use zod (or your language equivalent) and contract-first API development; this is my favourite bit tbh, I use orpc - make a single skill called "code" which describes the lifecycle: open a worktree, setup .env to guarantee no conflict with other agents (choose unused ports etc - docker is good here), write or update feature file (this is where you negotiate the spec), implement, validate (e.g. using playwright mcp), pre-push checks, push and wait for review, tear down and fast forward main - testcontainers is great for ensuring multiple agents can run tests that don't conflict Seriously I only have one skill that's it. Everything else is in the docs. I'm feeling very productive like this, in a "making good software" sense not a LoC sense.
View on HN · Topics
Your hostile tone is unfortunate, especially since my post was actually friendly. I was just trying to point why it is very likely the OP won't give you what you're asking so you're not left confused if he ends up ghosting you. Many people use the term harness to refer to the agent coding software (eg. Opencode, Claude Code...), i use this term more broadly to refer to the environment (set of skills, system prompts, constraints, memory, hooks etc...). What the OP is referring to is not just one giant skill. It's usually a comprehensive ecosystem of skills, bespoke tools to make certain agent tasks deterministic (eg localization), and so on. I've seen someone post Github repos in this thread, these can be very useful especially if you use the same tech stack, but you won't reach the level of productivity reported by successful teams unless you invest substantial time to build your own harness. But the way to do so is to do it progressively : start with something simple to address the need you have on day 1 . And then, turn recurring prompts into skills, turn recurring coding patterns and coding style recommendations into guidelines, turn repetivive tasks for which the LLM tends to build a python script that it occasionally gets wrong into a deterministic tool documented in a skill etc... And after a couple of days, weeks, and months, you'll have a very dependable harness giving you optimal productivity, without needing to invest weeks of work upfront or take the fun out of agent-assisted coding. Hope this helps.
View on HN · Topics
I agree. I followed this article for a repo I'm working on, and I had a very hard time inferring how, specifically, they implemented "providers" and enforced import layers. A sample repo would've been nice.
View on HN · Topics
It is a very valid question. My intution (no grounding) is to the model training. Optimizations traditionally have worked well in human wrote software with either experience of the developer , usage of architectural patterns or a second ir third pass of fine tuning. In case of model written code - (e/p one token at a time), only possible orchitectural optimization is either with a strict guardrail on patterns to use for a specific implementation OR by giving a second or third optmization path. All of which burns more tokens, but can lead to better software.
View on HN · Topics
> The diagram below shows the rule: within each business domain (e.g. App Settings), code can only depend “forward” through a fixed set of layers (Types → Config → Repo → Service → Runtime → UI). Cross-cutting concerns (auth, connectors, telemetry, feature flags) enter through a single explicit interface: Providers. Anything else is disallowed and enforced mechanically. Can anyone give me a simplified explanation of what they’re saying here? Having some trouble understanding.
View on HN · Topics
As other commenters have clarified, it's about layering, separation of concerns etc. Goes by many names. One such terminology here: https://en.wikipedia.org/wiki/Hexagonal_architecture_(softwa... . DI frameworks use terminology like "Provider": https://en.wikipedia.org/wiki/Dependency_injection#Injectors
View on HN · Topics
They're describing a layered architecture enforced by some script in CI. For example, if you had a `backend`, `common`, and `frontend` package, you would be OK having backend/frontend depending on common, but you wouldn't want common depending on backend/frontend or backend/frontend depending on each other. If you think about JavaScript, there is nothing stopping your dependency graph from becoming spaghetti. It sounds like they built static analysis to enforce rules. Some languages have this built in like Java (Project Jigsaw), Go, and Rust. JavaScript, Python, etc. have no such feature. It's really nothing special -- it has existed before. It just becomes a _lot_ more important with agents since they produce a lot of code, and it is good to have lots of static analysis when heavily utilizing agents. They mention this in the article: > This is the kind of architecture you usually postpone until you have hundreds of engineers. With coding agents, it’s an early prerequisite: the constraints are what allows speed without decay or architectural drift.
View on HN · Topics
IIUC its just strict separation of concerns Eg UI cannot reach down and directly read config files Configs must be only read by (im assuming) a storage interface layer called repo There’s a strict directionality of dependency Somewhat similar to ports and adaptors but presumably more strictly enforced by deterministic linters