A good pattern for this is to use the Unity serialization to inject all the references and initialized values you need. This allows Unity track all the dependencies and load everything you need.
Unity is still crap at loading things in order but you simply have to make peace with that. Instead you have to write a bit more defensively and assume that your code will get initialized in a random order. As long as you use Unity serialization all the references will be set when the lifecycle callbacks fire.
You can
- Use coroutine Starts to wait for initialization
- Ensure all the inits are done in Awake so things are ready during Start
- You can also move away form initialization towards a more lazy pattern and make sure any calls into your behavior are asynchronous and include self init when needed.
It is a bit clunky but you can get used to it. Lean into the idea of behaviors being asynchronous actors and it makes a bit more sense.