"On the thread side, deadlocks can be a problem. You don't get any help from the language in keeping your lock hierarchy consistent."
The 98% solution to this problem is to dodge it entirely. Communicate with things like channels, mailboxes, and actors, or other higher-level primitives. This is how Go programs hold together in general despite not even having as much support as Rust does for this sort of analysis.
The rule I've given to my teams is, never take more than one lock. As soon as you think you need to, move to an actor-ownen-resource or something. The "real" rule (as I'm sure you know, but for others) with multiple locks is "always take them in the same deterministic order" (and even that's a summary, it really also ought to discuss how only mutexes that can ever be taken at the same time have to be considered in order, and heck for all I know it gets complicated beyond that too; at this point I bug out entirely) but this is, in my opinion, the true source of "thread hell" as written about in the 90s. I don't think it was threading that was intrinsically the problem; yes, threads are more complicated than a single thread, but they're managable with some reasonable guard rails. What was insane was trying to do everything with locks.
In theory, this rule breaks down at some point because of some transactionality need or another; in practice, I've gotten very far with it between a combination of being able to move transactionality into a single actor in the program or offload it to a transactionally-safe external DB. For which, much thanks to the wizards who program those, test those, and make them just a primitive I can dip into when I need it. Life savers, those folk.