Indeed if you pass by value/use immutability where feasible, you already avoid most of the issues I’m warning against, so it sounds like you found a sensible way to apply it while avoiding the pitfalls.
> If I have to give a taxi driver a jerrycan full of petrol, that's the definition of a leaky abstraction... Possibly literally in this case.
:D
My main point in support of OOP is that OOP doesn't prevent you from making all your 'movable state' into raw messages. Properly encapsulated instance state is actually not dangerous nor unpredictable. What I like about OOP is that it offers some additional benefits in terms of high cohesion and loose coupling because co-locating logic with related state helps to write high cohesion, loosely coupled modules. It allows my messages and function/method signatures to be even leaner than FP allows.
If you want to catch a taxi to the airport, FP still demands that you bring a jerrycan of fuel to give to your taxi driver, the only restriction is that he cannot alter it (no mutations). This has some benefits if you then plan to re-use that same jerrycan of fuel for your airplane too... But is that how the world works?
I generally agree with this article. That's why I tend to write everything in 1 file at the beginning and wait for the file size to become a problem before breaking things up.
There are many, many, many different ways to slice things up and if you don't have a complete understanding of your business domain and possible future requirement changes, there is no way you will come up with the best abstractions and it's going to cost you dearly in the medium and long term.
A lot of developers throw their arms up and say stuff like "We cannot anticipate future requirement changes"... Well of course, not on day 1 of your new system!!! You shouldn't be creating complex abstractions from the beginning when you haven't fully absorbed the problem domain. You're locking yourself into anti-patterns and lots of busy-work by forcing yourself to constantly re-imagine your flawed original vision. It's easier to come up with a good vision for the future if you do it from scratch without conceptual baggage. Otherwise, you're just seeding bias into the project. Once you have absorbed it, you will see, you CAN predict many possible requirement changes. It will impact your architecture positively.
Coming up with good abstractions is really difficult. It's not about intelligence because even top engineers working in big tech struggle with it. Most of the working code we see is spaghetti.