Thanks (and to the other comment who suggested the same talk).
This was useful.
The tl;dr for anyone who can't spend an hour watching (why are so many Unity things done as videos?!?! so infuriating!) is that you use "Scriptable Objects" (new to me - this is a first-class thing in Unity) to store common state and so break statically defined dependencies between Game Objects by having them share the Scriptable Object rather than reference each other.
So you might have a Scriptable Object that contains the player's score/coins picked-up/zombies killed etc. On its own this is conceptually basically just a number/several values/etc wrapped in a small singleton class. You then reference that Scriptable Object in your player game object/script that will mutate that value. Anything else that needs to know the score/coins picked-up/zombies killed (e.g. UI code) references the same Scriptable Object.
So now two separate parts of the game share the state without being statically bound to each other (they are only bound to the Scriptable Object). Plug in as many or as few new components all using the same state stored in the same Scriptable Object and nothing else needs to change as they only share the Scriptable Object. No references to other game objects required.
You can do the same for events - create a Scriptable Object for an event (e.g. PlayerHurt or PlayerShoot etc). Since Scriptable Objects can contain code like any other class, you can have GameObjects register their interest in this shared singleton event by calling "PlayerHurt.RegisterListener(this)" etc, and then have another GameObject trigger/raise/activate the event ScriptableObject ("PlayerHurt.Trigger()" etc). Neither side know about each other, so you can plug in as many or as few listeners as you want. The talk uses some extra cleverness to use a UnityEvent to allow for a function call in reaction to the event being triggered - this means a lot can be done directly in the unity editor, but that seems separate from the main secret-sauce of the scriptable object as the mechanism for shared state.
Site here that basically repeats the video nearly verbatim: https://unity.com/how-to/architect-game-code-scriptable-obje...
Cheers!