I hear "clean architecture" and immediately think "rigid systems" and "unnecessary abstractions." My current view is that systems are "living things" and there is no "one size fits all" approach. I originally saw this idea (or something similar) in the "Livable Code" talk by Sarah Mei.
I do think "the Rails way" is a good start for web apps (and you can use it on most fullstack frameworks, provided baseline features). There's no need to apply the same rigid patterns to the entire codebase in order to "prevent chaos". By doing that, we end up adding accidental complexity to the system, increasing entropy and making simple things unnecessarily complicated.
I'm not saying to embrace the chaos, though. And that's not what "the Rails way" means to me. To me, it's about making only what's absolutely necessary and trying to build things as simple as possible. There will be complexity, that's not avoidable. Otherwise, the system wouldn't be necessary in the first place. But we can encapsulate complexity and keep it away of the main stage of the application into a package or internal module, leaving our "userland" code as simple as possible - even when we can't fully prevent leaky abstractions.
That's what I understand from "Conceptual Compression": pushing the complexity away of the "userland" code, making abstractions to simplify the core - even if they are leaky. DHH also talks about that in "Integrated systems for integrated programmers": instead of dealing with a bloated software, our goal should be to compress the domain so a single programmer can reason about it.
One of the ways we can do that is with "The Citadel" pattern. It's OK to extract complexity into external services/plugins. Just don't go "micro". Some obvious things that can be extracted are accidental complexity that deals with infrastructure things. Stuff that leaks into the "core" domain, but are not really part of the model.
That's how I like to think things like the Action Mailbox gem were invented. They had the need to deal with the complexity of receiving and sending emails in HEY (it's an email system, after all), so they created an abstraction to integrate the framework with a system that already handles it well, Postfix, instead of trying to handle it all in Ruby on Rails (I don't know if that's the real story.)
Somehow the idea of "ports & adapters" (clean architecture, hexagonal architecture, etc.) isn't that appealing to me. I mean, it makes sense in theory, but in practice... well, I usually work on web apps, so I hardly ever would have to port a feature to the CLI. Maybe that's my bias. Maybe the components provided by a framework are modern representations of "ports & adapters." I don't know.
Anyways, I just wanted to voice this out. If you have any resources on any of this that you think would either change my mind entirely or back this all up, send it my way.
---
This post was originally drafted on Twitter in 2020. I've tweaked it and posted it here instead.