In an AI driven world CLEAN Architecture becomes even more powerful than it used to be. And as usual AI does a great job of amplifying both the pros and the cons of something like CLEAN Arch.
## Benefits of CLEAN
By splitting your code up into layers there are some key benefits you gain:
1. "Testing First" architecture. Each Layer becomes a testable chunk by mocking/faking attached layers, allowing input/output driven test in most situations
2. Modular Structure. Clean Arch is designed with swappable layers in mind (remote/local datasources, react-native/jetpack compose)
3. Domain Driven Design. The domain is "all that matters". Other layers use or implement the domain layer. Decisions are made at the domain layer and then others "figure it out" to adhere to the all powerful domain!!!
At least those were the reasons I used to pitch CLEAN Arch to various stakeholders at companies!
##### Clean Architecture was always about things like making code testable. For me it was also about how I structured work.
Especially when managing larger teams of developers. Think squads/pillars
A larger group (developers, designers, PMs) that then make up smaller teams (iOS, Android, Web, Backend)
Squads would sync up early in a sprint/set of work to design/decide on the domain layer (this would be cross profession/platform). Once this is finished each team would go off on their own to implement the updates in their own way!
This could mean a domain layer per platform or a shared layer (via kotlin multiplatform/react-native) depending on your structure.
And even then these individual teams (web, ios, android, backend) would then split their work in a similar way.
### Squad Scope
```mermaid
flowchart TD
presentation["Presentation (Front End)"]
domain["Domain (Json/Rest Models)"]
data["Data (Backend)"]
presentation -->|uses| domain
data -->|implements| domain
```
### Front End Team Scope
```mermaid
flowchart TD
presentation["Presentation (React, Jetpack Compose, SwiftUI)"]
domain["Domain (Kotlin/Swift/JS models)"]
data["Data (Http Client)"]
presentation -->|uses| domain
data -->|implements| domain
```
### Backend Team Scope
```mermaid
flowchart TD
presentation["Presentation (Rest Server)"]
domain["Domain (Language Models)"]
data["Data (Local DB, Other Servers)"]
presentation -->|uses| domain
data -->|implements| domain
```
One way that might help conceptualizing this is that the presentation layer is whatever piece of your product/scope is going to be *client* facing. The client could be a user, it could be API consumers, it could be other layers of your code!
### So the work would go like so:
1. Squad alignment for Squad Domain layer
2. Team alignment for Team Domain layer (preferably this is *as aligned* as possible)
3. Team splits tasks based on layers (one to implement defined domain, one for UI, one for Data)
4. Go off and Work!!!!
Note: it is possible to even split this further up within the layers of the team!
##### Front End Presentation Layer Scope
Presentation: Composables/Views
Domain: State/Events
Data: Interactor/ViewModel/Controller
```mermaid
flowchart TD
presentation["Presentation (Composables/Views)"]
domain["Domain (State/Events))"]
data["Data (Interactor/ViewModel/Controller)"]
presentation -->|uses| domain
data -->|implements| domain
```
#### Product/Design Scope (Non Eng)
Presentation/Data don't exist here in the exact same way. But problems are still centered around the domain layer and expand outwards (or inwards).
Wanting to update designs may have domain layer impact (or at least will have presentation scoped domain changes). Or domain restrictions will impact design.
Product research/data insights may be required. What information from the backend do we have access to in the domain layer? How is it structured?
### How this applies to AI
This (for me) has since worked out **very** well when starting to apply coding agents (opencode, claude) to my workflows/codebases.
This has allowed me to work in a very similar structure to my previous (larger) teams, and let me focus on the things that I really care about.
I decide on and build the domain layer
Agents can be sent out to problems in a similar layered way:
- data layer for integrating Kotlin UI with a Rust daemon (for dekky).
- Integrate the mapping between data/implementation and domain layer
- Scaffold out an edit page for a domain model for an admin console
- Ensure test coverage of data -> domain layer mapping
- Condense large sets of analytics data to help with insights
This means I get to focus on the parts I *really* care about!
- That really fun animation that will wow the users
- That pesky memory management of managing a camera that I know an agent will mess up
- Catching up on the happenings of Bikini Bottom
## Concerns
Now as usual, not everything is all holly jolly in AI land.
There are some major downsides of Clean Architecture (which also get amplified under AI)
##### Golden Hammer
CLEAN can be used as a "golden hammer" when every problem looks like a nail. While it "may fit" it could be far from the correct solution (ex: games, anything high perf)
##### Always Abstracting
Abstraction and layered approach can lead to a "always abstract" mentality. Leading to ever growing layers of abstraction that lead to the spaghetti code we were trying to avoid in the first place. (Maybe we *don't* need 4 new interfaces for a button click)
And while these things are very good for human programmers (compartmentalization, visualization... etc etc) its not very good for computers (the more we abstract the better compilers have to be to get the "goods").
Which basically add up to.... It becomes easy to write code for the sake of writing code, vs solving the engineering problem at hand. Which sounds like a PERFECT situation for AI to generate code ad infinitum. This turns into a classic case of knowing WHEN to abstract and WHY to abstract vs just THAT you sometimes, should abstract. And again, the more we abstract, the more the computer needs to work to figure out the actual problem we are trying to solve
##### AI Models are people pleasers
If you ask AI to write code it will write LOTS of code for you, it rarely knows when to stop! So in systems like this its knowing what code *not* to write that becomes the real superpower (ie. when to abstract, when to add, when to split). Especially when being a "good programmer" is more and more being tied to number of lines produced. It will definitely generate those lines for you.
This is where YOU as the developer/AI tamer become the key player at the table. You decide when layers are created/abstracted. And in most cases it turns into a task of herding curious kittens (A way my partner likes to explain project management)
## Conclusion
By structuring my apps by using CLEAN Architecture as a guideline. It has allowed me to structure and scope the work of my project in an architecture/code driven way. Giving agents scoped access to layers, with boundaries of what they can touch. But all while being aware that they will try to go a bit crazy, so its on me to have the true "vision" forward and ensure the kittens dont end up pushing the code-base off a cliff. Or.... at least only one layer might go off a cliff 😅
### Future Ideas
There are lots of ways I can expand on these ideas as well. Especially when thinking about agent specific context.
- AGENT.md's per layer/domain to help really scope knowledge/context?
- Agents that then dispatch other agents in my Squad/Team structured way? For me to then clean up later?
This is always conflicting with my strive to know "whats going on" in my projects. So I will always keep this in mind and try not to delegate too much.
Chaos is inevitable when you lose control of kittens.
## Where to find me/my stuff:
Vids/Streams: Mon, Wed, Thursday 11 -> 4pm EST
https://www.youtube.com/@dpaltv
Projects:
http://github.com/dpaltv
http://github.com/eholtrop
Like what I do?
http://patreon.dpal.tv
http://kofi.dpal.tv