asyncio is still really wild in 2025. I haven't actively written asyncio code in a while but now that I have done it for a week agian, I noticed that most problems from when I used it last are still unresolved.
Problem 1: asyncio.create_task is a massive footgun because it can silently make tasks disappear. You need to hold a strong reference to tasks!
Problem 2: The old issue of write() being a hazard is still true. This innocent example is wrong and you need to follow the write() calls with await drain() to not have buffer bloat that can bring you down:
Problem 3: You probably already know that with timeout() with a generator inside is bad, should not be used. But in fact pretty much all async generators that involve cancellation scopes are broken. This innocent code will also not work (see this motivating example from PEP 789):
Problem 4: pytest-asyncio mostly works, but fixtures are a whole new beast. That's because you can both end up accidentally running in the wrong loop, but you also can build yourself into a deadlock easily. I'm not even sure what exactly the cause is, but there are plenty of open issues where people end up in deadlocks.
Problem 5: Speaking of deadlocks, they are super easy to create with asyncio's idea of cancellation paired with the new structured concurrency. For instance if you use TaskGroup and you spawn a task in there that uses aiofiles (which cannot cancel) and you have another task that throws an exception you won't see the error. That's because it tries to cancel the aiofiles read (which is non cancellable) and if no more reads come in you just lock until someone hard quits the program.
In general even if you can write asyncio code, writing reliable asyncio code is so much harder.
----
Maybe not quite asyncio related, but there is also trio. But despite the existence of anyio, in practice some code does not yet support it. As an example the the popular asyncpg driver does not support anyio so you're mostly locked into asyncio anyways unless you are really determined.
And then there are lots of DX issues like the standard Python console not supporting asyncio or there not being basic file IO in the standard library.
But then why not use sync code? In practice that becomes harder and harder because of how much of the ecosystem forces async on you. Using sync code with starlette for instance mostly works, but for all IO you will need to use async anyways. WSGI is a dying protocol at this point and it's not clear if there are people pushing that forward.
So even though asyncio is a decade old at this point, it's still so easy to misuse and it makes me sad.