"The way it's handled is that the process where the previous socket was connected remains in place until it terminates but all new sockets connect to the new code."
That would imply that two separate processes are bound to the same port right? I thought that was not possible.
No, usually there's an accept process spawning working processes and passing the connection off to it.
Erlang can have two versions of a module present in memory at a time. At the moment of a code upgrade, the processes working with existing connections continue to use the old code while newly accepted connections are passed to the version of the module that was loaded in between connection accepts.
While there are certain socket options to share a port - two processes can share a port easily in another elegant way; just call fork().
One way of doing such upgrades is then for the existing code to fork() then exec() the new version. The existing old code closes the original server socket, which leaves accepting new requests to the new code. When all in-flight requests served by the old code is finished, that process can exit.
Presumably the original server process uses exec() to start the new process, which inherits the open socket handles, and the descriptor is passed as an argument to the new process.
That would imply that two separate processes are bound to the same port right? I thought that was not possible.