Decomposing Problems, Composing Solutions
This is the very essence of engineering. It's the thing you'll do every single day for the rest of your career. Learn to look at the complexities that you are faced with and break them down to composable solutions.
Here's an example of what I mean: I am building a multi-player browser game with a friend of mine. I'm using Firebase for authentication and data storage, WebRTC data channels for transmitting character data, and media streams to allow voice chatting. Opening up a voice channel between players requires each player to log in, wait for some user interaction before starting the Audio API, register for a peer ID with a brokering service, write the peer ID to Firebase, then check for any players who've joined the game before them and initiate a call using that player's peer ID found in Firebase. Because these events don't happen in a deterministic order, the early proof-of-concept that I wrote was very difficult to follow and hard to debug.
But once I extracted these out into separate modules, exposing relevant methods and events for each, it became a lot easier to reason about. An
audio service that's only in charge of initializing the audio API, setting up the relevant context and mixing. A
peer service that's only in charge of brokering peer connections. A
voice-chat service that builds on both of them to route audio streams appropriately.
Get used to looking for the providers that your individual features require. How can you break your logic down into separate concerns, then compose them into your desired solution?