In the same way that GC eliminates the need for manual memory management.
Sometimes it's not enough and you have to 'do it by hand', but generally if you're working in a system that has GC, freeing memory is not something that you think of often.
The BEAM is designed for building distributed, fault tolerant systems in the sense that these type of concerns are first class objects, as compared to having them as external libraries (eg. Kafka) or completely outside of the system (eg. Kubernetes).
The three points the author lists in the beginning of the article are already built in and their behavior are described rather than implemented, which is what I think OP meant with not having to 'intentionally create graceful shutdown routines'.
I really don't see how what you are describing has anything to do with the graceful shutdown strategies/tips mentioned in the post.
- Some applications want to instantly terminate upon receiving kill sigs, others want to handle them, OP shows how to handle them
- In the case of HTTP servers, you want to stop listening for new requests, but finish handling current ones under a timer. TBF, OPs post actually handles that badly with a time.Sleep when there's a running connection, instead of using a sync.WaitGroup like most applications would want to do
- Regardless if the application is GCd or not, you probably want to still manually close connections, so you can handle any possible errors (a lot of connections stuff flushes data on close)
Thread OPs comment was pointing out that in Elixir there is no need to manually implement these strategies as they already exist within OTP as first class members on the BEAM.
Blog post author has to hand roll these, including picking the wrong solution with time.Sleep as you mentioned.
My analogy with GC was in that spirit; if GC is built in, you don't need custom allocators, memory debuggers etc 99% of the time because you won't be poking around memory the same way that you would in say C. Malloc/free still happens.
Likewise, graceful shutdown, trapping signals, restarting queues, managing restart strategies for subsystems, service monitoring, timeouts, retries, fault recovery, caching, system wide (as in distributed) error handling, system wide debugging, system wide tracing... and so on, are already there on the BEAM.
This is not the case for other runtimes. Instead, to the extent that you can achieve these functionalities from within your runtime at all (without relying on completely external software like Kubernetes, Redis, Datadog etc), you do so by glueing together a tonne of libraries that might or might not gel nicely.
The BEAM is built specifically for the domain "send many small but important messages across the world without falling over", and it shows. They've been incrementally improving it for some ~35 years, there's very few known unknowns left.
Sometimes it's not enough and you have to 'do it by hand', but generally if you're working in a system that has GC, freeing memory is not something that you think of often.
The BEAM is designed for building distributed, fault tolerant systems in the sense that these type of concerns are first class objects, as compared to having them as external libraries (eg. Kafka) or completely outside of the system (eg. Kubernetes).
The three points the author lists in the beginning of the article are already built in and their behavior are described rather than implemented, which is what I think OP meant with not having to 'intentionally create graceful shutdown routines'.