The difference is in how easy it is to detect the cause of problems.
Mistakes like wrong function names are mostly easy to find and fix.
Mistakes when using core.async can be very hard to track down.
I think you mean <! and <!! - first one is parking take for use inside go blocks (lightweight threads) and second is blocking take for use outside them. I have sometimes wondered if they should have just called them, like, "take!" and "take!!" or even better "take-parking" and "take-blocking". Even if personally it was not hard for me to learn them, versus the whole model of async and the rules around go blocks.
I haven't heard complaints about the thread pool before, I thought it just matched your number of cores by default but could be configured. I do know if you do blocking takes (<!!) where you're supposed to do parking takes (<!) the lightweight threads block the entire parent "real" thread and you can get thread exhaustion, maybe it was that?