Earlier quoted context omitted.
Echoing the thoughts of the only current sibling comment: lots of "serious" developers (way to gatekeep here) definitely use coroutines, when they make sense. As mentioned, it's one of the best ways to have something update each frame for a short period of time, then neatly go away when it's not needed anymore. Very often, the tiny performance hit you take is completely outweighed by the maintanability/convenience.
...and then crash when any object it was using gets deleted while it's still running, like when the game changes scenes, but it becomes a manual, error-prone process to track down and stop all the coroutines holding on to references, that costs much more effort than it saves. I've been a serious Unity developer for 16 years, and I avoid coroutines like the plague, just like other architectural mistakes like stringly…
The same code in a coroutine hits the same lifecycle failures as Update() anyway. You don't gain any safety by moving it to Update().
> No structured cancellation.
Call StopCoroutine with the Coroutine object returned by StartCoroutine. Of course you can just pass around a cancellation token type thing as well.
> Hidden allocation/GC from yield instructions.
Hidden how? You're calling `new` or you're not.
Instead of fighting them, you should just learn how to use coroutines. They're a lot nicer than complicated logic in Update().