The only part I don't really agree with, is the avoidance of app separation and signals. It's one of the cleanest ways to decouple your modules and keep some level of sanity in a medium-sized codebase. It comes down to deciding what parts really need to depend on others and be very explicit about that.

I generally end up with a few core apps with the main data objects that a lot of other "parallel" ones depend on, a bit "star shaped". And then a few "aggregator" apps that cover functionality that needs to work across multiple of these domains. I see it all as an extension of how you think about your data model.

I always wonder about signals. I found them incredibly attractive from a code cleanliness standpoint, but every team I’ve joined avoided them either because they got burnt by them or because of superstition from blog posts by people who got burnt by them. It may be a cognitive load thing, that in smaller codebases it’s easy enough to remember “these three things fire a signal when saved”, but bites you in the ass when trying to make a quick patch for a bug in production late in the day because a customer called up screaming.

I do understand the "invisible side effects" side. I do try to be conservative in what I do in signal listeners.

I do what I must in the atomic context, and trigger celery tasks for everything else.