I’d quibble a bit with the point about buffer. In my experience, what often happens is: since we have the buffer, we think we have plenty of time. So we take our time. And run through the buffer, even if we didn’t need it.
The approach I usually take is try to get a 90% confidence estimate on the task: how much time do you need so you are 90% confident you will get it done on time. This then leads to a conversation about what the things are that make us uncertain about the project, which is largely the list you gave: needing to work with new technology, error prone code, addressing tech debt, and so on. I’ve seen two benefits to this approach: (1) now that you’ve identified specific risks, you schedule spikes/proof-of-concepts to address each of these risks, and (2) you end up with a more meaningful buffer (sometimes less, sometimes much larger, than 30%).