This still doesn't handle restarting crashed services, and it still is true if you need to make init scripts for your own services outside of the ports tree.
It took me a long time to come to terms with systemd, but I am very glad that I did. For me it makes defining services both easy and reliable.
I looked at one of my FreeBSD servers to see how it was handled in the case of ZNC, an IRC bouncer that I use. ZNC doesn't produce a pid file on startup, so the FreeBSD RC framework tries to find a matching process in the output of 'ps'. [1] As soon as you attempt to run multiple instances of a given service, this falls over completely. [1]
daemon(8) helps -- it handles restarting of processes if they crash, and it can manage pid files and prevent them from getting stale. Nothing on my FreeBSD system uses it. The unbound (dns cache) port uses the pidfile option for rc.subr(8). Looking at how check_pidfile is implemented, it attempts to verify that the process represented by the pidfile matches the process name. Pids also wrap on FreeBSD, so you have a chance of a false positive match if you run multiple instances of a given daemon. I could, of course, change unbound's rc scripts to use daemon, but that feels like a lot of thinking about pidfiles for something that was taken care of a decade ago.
I do like FreeBSD, don't get me wrong, and I use it in my every day life. systemd solves problems for me, and I really like the way it manages process groups by utilizing kernel features.
1. https://docs.freebsd.org/en/articles/rc-scripting/#rcng-daemon
2. https://unix.stackexchange.com/questions/503150/rc-scripts-for-multiple-zope-instances-in-freebsd