Symbolic links need to be modified atomically. If you're halfway through writing them, they won't work.
The content of a symbolic link is pretty small (at most 4095 characters on Linux: the maximum length of a path to a file), so there would be little point in editing part of a symbolic link at the kernel level. Therefore the kernel does not offer any interface to edit a symbolic link, only an interface to create a new one, the symlink system call (plus the generic interface unlink to remove any file).
The symlink system call only creates a new symbolic link, it does not remove any existing file. This is annoying, but consistent with other system calls to create files such as open (which can create a new file or truncate an existing file, but not replace an existing file by a newly-created file) and mkdir.
In the shell, as you've discovered, while you can't replace a symbolic link atomically with the ln command (ln -sf unlinks the previous file then creates the symbolic link), you can do it by first creating a symbolic link under a temporary name and then moving it into place.
tmp=$(TMPDIR=$(dirname -- "$link") mktemp)
ln -sf -- "$target" "$tmp"
mv -f "$tmp" "$link"