NAT is only painful for applications that implement arbitrary inbound connections, such as peer-to-peer connections, and applications that use separate control/data connections. The former is understandable because it's necessary, the latter is arguably lazy application developers.
Peer-to-peer is largely solved with UPnP port forwarding, but most of these apps can also at least be configured to use a specific port range, education can then be configured on the router to forward.
Then the are the badly designed protocols. That's perhaps a bit harsh, they have their reasons, but using multiple ports is a bit lazy vs using encapsulated packets (eg where you can send either a control or data packet over a single connection, or even send multiple data streams concurrently, in both directions).
FTP is one example, where a separate port is used for data transfers. This is "simple" from the perspective that the data port just contains the raw file contents, no extra encoding, but very painful from a network point of view. I used to support web hosting customers around 2000, when ftp was commonly used for maintaining site content, and people didn't understand when to use PASV mode, server operators didn't always setup the firewall to allow inbound data ports, and occasionally you'd run into the worst case of both client and server behind NAT.
SIP is another example. It uses multiple streams, but is built assuming everything has a public IP. The message it uses to setup this stream includes the system's IP address, which is the private non-routable IP when behind NAT.
Contrast these to a multiplexing protocol like HTTP/2, where it's basically transparent to network operators (still just a single TCP connection on 80 or 443). There's many other examples of multiplex use in more proprietary systems: online gaming, for example, sends different packet types over a single connection -- it doesn't require a separate "movement" and "shoot" port for each connected player. Most sites/apps using web sockets also work like this, with each piece of data encapsulated inside a message wrapper (usually JSON).
I'm being a bit unfair, because FTP predates NAT, and SIP was being created around the same time -- though I'd argue SIP's assumptions about the network environment (and seeming compete ignorance of NAT) were both poor and unnecessary.