Interrupt handling in microcontrollers and FSM example
The first tactic is to architect the overall firmware so that it's OK for interrupts to occur at any time.  Having to turn off interrupts so that the foreground code can execute a atomic sequence should be done sparingly.  There is often a architectural way around it.
However, the machine is there to serve you, not the other way around.  General rules of thumb are only to keep bad programmers from writing really bad code.  It is far better to understand exactly how the machine works and then architect a good way to harness those capabilities to perform the desired task.
Keep in mind that unless you really are tight on cycles or memory locations (can certainly happen), that otherwise you want to optimize for clarity and maintainability of the code.  For example, if you have a 16 bit machine that updates a 32 bit counter in a clock tick interrupt, you need to make sure that when the foreground code reads the counter that the two halves of it are consistent.  One way is to shut off interrupts, read the two words, and then turn interrupts back on.  If interrupt latency isn't critical, then that's perfectly acceptable.
In the case where you must have low interrupt latentcy, you can, for example, read the high word, read the low word, read the high word again and repeat if it changed.  This slows down the foreground code a little, but adds no interrupt latency at all.  There are various little tricks.  Another might be to set a flag in the interrupt routine that indicates the counter must be incremented, then do that in the main event loop in the foreground code.  That works fine if the counter interrupt rate is slow enough so that the event loop will do the increment before the flag is set again.
Or, instead of a flag use a one-word counter.  The foreground code keeps a separate variable that contains the last counter it has updated the system to.  It does a unsigned subtract of the live counter minus the saved value to determine how many ticks at a time it has to handle.  This allows the foreground code to miss up to 2N-1 events at a time, where N is the number of bits in a native word the ALU can handle atomically.
Each method has its own set of advantages and disadvantages.  There is no single right answer.  Again, understand how the machine works, then you won't need rules of thumb.