In my opinion, the idea of true self-documenting code is a myth. I agree that the code itself is always going to be the most up-to-date explanation of what your code does, but I thing that it’s very hard for it to explain intent, which is the most vital aspect of comments. If it’s written properly, we already know what the code does, we just need to know why on earth it does it!
When you read a “self-documenting code”, you see what it is doing, but you cannot always guess why it is doing it in that particular way. There are tons of non-programming constraints like business logic, security, user demands etc. When you do maintenance, those background information become very important.
No matter how self-documenting a code is, if other alternate approaches were considered and discarded that information will get lost unless somebody comments the code with that information. Sometimes it’s just as important to know that an alternative was considered and why it was decided against it.
A particular line of code or a sub algorithm may be indeed self documenting but it’s purpose in the greater picture is simply not. Also, what my module does and what I intended it to do, can be very different (e.g. when there’s a bug in the code). Comments help future programmers identify that gap and close it appropriately.