But they aren't! If someone comes along and wants to run two copies of the app that share some object they can. And in a super straightforward way. As a maintenance programmer, I love these sorts of things. It's the difference between weeks of work and days.
I disagree. Nothing gets messy faster than a big bag of state that gets passed all over for no reason. This is no better than the global issue. The correct solution is to inject what you need and build convenience functions to reduce boilerplate. I have serious doubts about your weeks/days quip; my experience is diametrically opposite.
To be fair I more meant a giant tree of state where each module is responsible for managing the state for other modules it communicates with (for example a module will take it's bag of state for most every method; in an object oriented language - a luxury I don't often have - this bag of state is called an object (and methods that don't take it would be called static methods)). Not a flat state object (obviously terrible).
Fair enough, but my beef wasn't about the shape of the large amount of state or its organization, but rather that the whole thing is passed into everything when most things aren't needed. This makes it difficult to reason about what the true dependencies of a function are.
Yea, but passing a large object around is what I was talking about. Still not great, but when you deal with a 200k LoC library and all of it's API functions take that single large object, it's way better than having to mess with the global state of it. Like in one of those cases I can add naive multi-threading (two copies of the large state object), my own custom functions, and split out functionality for reuse by other systems. In the other I can't without understanding and likely rewriting the whole thing.
The number of params to a function doesn't seem like a particularly meaningful metric to optimize for. It seems like a proxy for something else at best.