> You lose all observability, unless you integrate a lot of "magic", for example making every value secretly use "immer".

Oh - of course. But I think Kea’s current syntax is also a kind of magic; but a different sort of magic making different tradeoffs. For example - the typescript typedef generation build step magic is a consequence of the current syntax. At the end of the day, a developer debugging any system that uses a framework may need to understand it’s internals to debug the issue. Reducing magic (or containing magic to a thin part of the framework) makes that step easier.

The kea internals will generate action objects from the `actions` call, and the `reducers` call provides a shorthand to responding to those actions. Listeners provides a way to spawn effects when an action occurs. These are compositions and abstractions over the Redux core. To figure out how they map to Redux, you need to either read the docs, or read the source.

I think you can map the example class I wrote to similar concepts - but instead of three+ different function calls to build a logic, you could do one, like `magical(classInstance)` - which would scan the instance and derive internal state machinery much the same way that kea’s `actions` and `reducers` iterate over their argument objects. Action properties translate to actions, state properties translate to reducers, and although listeners aren’t explicitly scannable, the effect callback inside an action can provide a similar API w/ breakpoint() within that scope.

Make the state properties only assignable inside an action function interior, and store their data inside Redux. Buffer writes to these properties until the end of the action internal function, and then flush them as a single Redux reducer change. Data mutation is bad - I agree! Deep-freeze the data those state properties point to in local development mode, and use a proxy that throws on invalid mutations like classInstance.users.push(…) to guide developers —- or use Immer as you suggest.

Dispatch calls to the action methods as Redux actions of the form { target: classInstance, action: methodName, payload: methodArgs }, and also build a reducer that will call the action function when the reducer receives that Redux action object.

This api is certainly more magical that kea’s api — but it’s still a composition on top of Redux. It has even more ”shorthand” for Redux. But would it be less maintainable? No matter the framework - Redux, Redux Toolkit, Kea, this monstrosity I just cooked up - your team needs to invest in culture and training about the right way to do things. There’s nothing AFAICT that kea-the-framework-code or redux-the-framework-code can do to prevent a developer from doing wild side-effects inside a reducer; likewise the sketch above needs culture and linters that guide developers to always wrap effects in action.effect, don’t use untracked private state, etc. Maybe the kea API structure makes it easier to build the cultural parts, but requires more boilerplate - that’s part of the trade off.

I guess the question I’m asking is, why is kea’s current position on the explicit-vs-magic spectrum the right global maximum for developer ergonomics & maintainability? kea advanced over raw Redux already, but why not go even further?