What's amazing to me is how uncommon state machines are in software, comparatively. In ASIC and FPGA design, it's so fundamental and ubiquitous. At least with synchronous designs.
I pushed for using state machines and more formal methods in the rewrite of a system that I and a number of other devs were hired for. The idea was turned down in favor of lots of ad hoc state and string constants, even though the system has a limited and distinct set of states on the main domain objects. People don't do it because people don't do it, and they're afraid others aren't going to understand it.
The vast majority of companies essentially code and design to the lowest common denominator so that their devs are fungible cogs.
The problem is that it's extremely easy to violate the boundaries of state machines. For example there's some small edge case, that would normally require you to completely re-create the state machine, but people bypass that by just escaping the state machine. In ASIC/FPGA, it's actually relatively hard to escape the state machine because escaping the state machine causes explosions in timing required and causes race conditions.
Code doesn't have fixed timing requirements, most of the time.
The vast majority of companies essentially code and design to the lowest common denominator so that their devs are fungible cogs.