> if you're passing in/out some monstrosity which has a structure that you can only really find out by reading the code
This is conflating multiple issues. Using generic collections (dictionaries, if you like) and then trying to work out what's in them. Modern stacks include a debugger, so figuring out what's in them is trivial in a given code path, if you have a test environment.
Figuring out how to use them (or how to not abuse them) when you have an immature development environment, is the issue at hand.
The author of the blog implies that they will necessarily be problematic. That's incorrect on it's face, but let's get into a codebase where we have no frame of reference, documentation, a limited dev environment, and/or the tests are missing/ignore the ramifications of the dictionary containing a variety. The question then becomes one of ascribing blame in these conversations:
1. is it the fault of the original programmer in selecting a dictionary? - No
2. is it the natural outcome of using a dictionary? - No
3. is there a compelling reason to use a dictionary at all (over a typed list)? - Yes and I would say more than one.
That's all aside to the issue of "what do you do now?" Well you do research and work, same as any of these tangled codebases (which may not be tangled at all). You break it up, you write tests and then you reason about the choices made. None of this is particularly special in regards to dictionaries. It's a particular practice that is sometimes expedient or efficient and that seems to offend the author's sensibilities. It's zealotry.