I disagree. If you have interrupt priorities then you can treat your interrupt service routines as the highest priority tasks. As an example lets imagine a drone controller which has two functions it needs to perform in real time: it needs to vary the PWM signal to the motor controller and it needs to acknowledge radio packets. An interrupt is raised when the accelerometer has new data and another is raised when the radio has received a packet. You could write ISRs which just queues the accelerometer data and radio packets so they can be dealt with by a flight control task and a radio task respectively. Alternatively, you could run the whole of the flight controller inside the accelerometer ISR. This reduces copying of data and context switches. Because the accelerometer ISR now takes longer you need to use the radio ISR to acknowledge the radio packets itself. You split the radio task into two tasks: a low priority task for processing commands and the ISR task which does the acknowledgements.
> Avoid blocking function calls
This can be rewritten as never call functions that block on an action of a lower priority task. It leads to priority inversion in normal tasks and deadlocks in ISRs.