More importantly though, software debouncing offers greater flexibility. For keyboards, you usually[1] want to implement an asymmetric "eager" mode, where a key press gets registered immediately and only the key release is debounced. Since software usually does stuff on the key down event, this works to reduce latency.
[1] Well, that's what the various enthusiast mechanical keyboard firmwares do, I am not so sure that generic $random_corp keyboards do care...
In fact it's more customizable for quicker response than hardware RC debouncing.
There is, however, something wrong with shitty debouncing software.
But if you want to reduce hardware cost, you can eliminate many capacitors and resistors by doing it in firmware instead.
Cherry once made a software/hardware hybrid but it didn't have any advantages compared to software debouncing, and it needed more power, so there was only one model.
If I understand it correctly, debouncing with a capacitor delay events somewhat. With software debouncing (done right) you are only required to have a minimum time between the press and release events.
Most Microcontrollers have the pullup resistor built in. Modern ones even let you turn the pull-up feature on and off per pin with a SFR (a special register /fixed memory address you can write to where you change hardware settings). Often you can also choose between reading a pin or driving it as an output.
The most important thing to know about debouncing is that any hardware switch bounces from 50-200 times over a very very short interval when you press it. You can use an oscilloscope to see that. The bouncing is not as bad on release but I imagine it still exists there too.
Another fun hardware trick is that when you turn off a magnet (like a relay) you can get a massive over-voltage pulse back (10x typical drive) for a moment. You need to protect yourself from that, for instance with a Schottky diode. Here's a discussion on that fun detail [1].
[1] https://forum.allaboutcircuits.com/threads/reduce-voltage-sp...