Dual Stack is not always the answer
Many IPv4 networks, when they transition to IPv6, use a "dual stack" approach, where they run both IPv4 and IPv6 stacks at the same time. However, this also means that each host must have at least one IPv4 and one IPv6 address. Given that there are only 232 IP addresses in IPv4 and 224 addresses in private networks, and 264 addresses in a /64 of IPv6, a purely dual-stack network to scale would easily run out of IPv4 addresses long before its IPv6 supply runs short, short of adding several layers of IPv4 (carrier-grade) NAT. In terms of chemistry, IPv4 is sort of like the "limiting reactant" or "limiting reagent".
On the other hand, there is a transition technology known as NAT64. It involves mapping a /96 of IPv6 to a special translator, which in turn maps it to the IPv4 address space. The most common NAT64 implementations are stateless, which are the fastest, but requires static mapping of IPv6 addresses to IPv4, which only makes the problem worse, since you still need a lot of IPv4 addresses. On the other hand, stateful NAT64 keeps dynamic mappings of IPv6 space to IPv4 space, which means that it can handle requests for many IPv6 devices at the same time without any static mapping.
Even in the world of IPv4, IPv4 NAT is stateful. On Linux, NAT is implemented using the iptables connection tracking module, which keeps mappings of private IPv4 address and port 4-tuples, and converts each of those tuples into ones involving public IPv4 addresses, and then relaying both of these connections between each other.
My approach to this problem is a hybrid stateful NAT64/dual stack approach. We have a stateful NAT64 translator, and the only requirement is that the networks behind it have a static route (not learned via neighbor discovery) to the translator. On the other hand, conventional NAT44 is also available. This allows dual-stacked hosts to work correctly like any other dual stack network, but it also facilitates IPv6-only hosts to connect to the IPv4 internet. The important thing here is that even if a host supports IPv6, it should not be given an IPv4 address unless it absolutely requires it. Our implementation currently only supports TCP relaying, and many IoT devices likely only connect to services via ports 80 and 443, so these devices can be IPv6 only. On the other hand, primary desktops should be IPv4/IPv6 dual stack, due to the variety of computer programs which can run on these systems.
A NAT64 implementation allows us to run "IPv6-first" networks without having to worry about the limitations of IPv4. This makes networks more scalable, easier to manage, and most importantly, facilitates the growth of the IPv6 Internet.
Besides NAT64, proxy servers are also a useful tool to connect a single-stack IPv4-only or IPv6-only device to the dual-stack internet. I'm not talking about open proxies, but rather, local network HTTP or SOCKS4/5 proxies that support both IPv4 and IPv6. In SOCKS4a, SOCKS5, and HTTP, the client only sends the domain name of the requested resource, and a direct connection is made to the IP address of that resource. The client doesn't even see the IP address, so the client doesn't even need to have IPv6 support to reach an IPv6-only website. Proxy servers are widely supported among many devices, especially embedded devices, so they should be a very attractive option for IPv6 deployment.