> How do you deal with arbitrary objects being passed around and not know what's potentially coming from where?
Honestly, by trusting that those objects conform to what things are documented to expect and using operations that will naturally raise an error for unexpected cases.
Basically, you don't depend on compile-time or IDE-time checks, you depend more heavily on tests.
It allows for much more of a direct approach. E.g. if something's documented to accept a sequence, I don't care whether it's a list, tuple, some custom object, etc, I just need it to have certain properties like iteration and ability to index.
You write things to expect certain abstract categories of types and do things that will raise errors if they're not in the category you expect. The downside is that nothing is enforcing that in an IDE or at compile time. As a result, you need more integration tests.
In some ways, yes, you're more at the mercy of other developers behaving well. The flip side to that is more flexibility and less duplication.