Kalvin's strategy is very good, and I wouldn't call it "advanced"; quite the opposite, it's easy to do.
My take is, ignore the general advice of "keep ISRs short and make them set flags; process flags in main()". This may work in some cases, but is generally undeterministic, hard to analyze, and forces much more shared data between tasks than would be strictly necessary.
For example, if you follow the typical advice and program an UART interrupt, set a flag in ISR, and poll for that flag in the main loop, you could have just polled the peripheral interrupt flag to begin with, without writing or enabling the ISR in the first place. Or, you could use DMA to stream the data to memory, directly, and process it at a convenient time. But if you need to do it when the event happens, why not do it in the ISR, completely? Only if you have two separable actions, a timing-critical one, and a irrelevant-timing heavy one, then it makes sense to set flags and continue processing in main(). But this isn't any kind of default case. And even then, it would be more deterministic and easier to follow to demote the interrupt priority by creating a lower-priority software interrupt that continues processing.
Interrupt priorities and pre-empting interrupts make it easy to run time- or event-triggered tasks right whenever needed.
It's a good idea to run all code that needs any sort of realtime deterministic behavior in the interrupts. And contrary to the usual poor advice, there is no reason to make the interrupt handlers extremely short. Say you get a new ADC datapoint, and need to do some math upon it, to then decide whether to turn an IO on or off. Do all that in the ISR. Maybe your ISR takes worst case 1ms but you need to react to a comparator event in 10µs? Write a higher-priority ISR for the comparator event!
Maybe you have a state machine, run in ISR, triggered by SPI transfers from a sensor, and get a full data packet after the 17th state? At that state, generate a lower-priority software interrupt which takes over processing the full dataset. Now your longer processing function is out of the way, but you still have better determinism and safety against code changes compared to if you just set a flag and looked at it at some random place in your already long main loop.
If you go this way, the main() ends up doing the initialization and then either remains in empty while(1) loop, or does some procedural logic where timing and jitter is completely irrelevant, as long as it gets enough CPU time. Which is quite easy to calculate from ISR repetition rates and worst-case processing times.