As a comment mentions, your question is pretty broad.
In my experience the main problem is the fact that you have multiple different teams working on all the services. So that is the main issue my answer tries to attack:
Central definition of deployment versions
The first approach would be to leave the teams disjoint, including their separate clusters, and sticking with your approach to have dual clusters for each team (note that having separate namespaces would be a little more lightweight, but may make everything a bit more complicated depending on how you have everything set up, especially concerning config and secret management).
But you would then chose a central approach for deploying everything. You could have a central ArgoCD repository (a single repo for all your clusters/teams), and each of the separate clusters could reference the same ArgoCD repo. You could use one ApplicationSet per service (or per cluster, or per team, depending on how big everything is and how much individual control you want to have); and you would use an appropriate directory structure within that repo to keep everything well separated.
This repo would be managed by a single team in the beginning, at least, and possibly forever. This single team would be responsible for discussing changes, backward compatibility problems and so on and forth between the other teams.
This would mean that the individual teams would need to be very clear with their communication (i.e., write proper Changelogs, have proper semantic versioning and so on and forth), which is good practice anyways. And there would be of course some communication overhead, and they would give up a little bit of autonomy regarding their deployments, but depending on your situation that may all be acceptable.
Eventually, when everything has settled in, you might chose to reduce the role of the central team and try to distribute the work of updating the central repo over the teams again, if you trust them not to break down in chaos.
Hardcore backward compatibility
The second approach would be to let everybody deploy their own stuff, but work with very strict guidelines that breaking changes can never be done on existing APIs. Everytime an API changes in any way that means it would not be backward compatible, you would enforce the API name to change (e.g., increase ".../v1/..." in the REST-API URL to ".../v2/..." and so on and forth). Every service would be forced, in this scenario, to support older versions indefinitely. You would then maybe have regularly (quarterly, no matter) meetings where you could officially phase out old versions, but only when there are no users of said versions anymore.
You could achieve this by either keeping separate implementations in the code itself alive, or by keeping older images (and deployments of them) around and solving the issue on the k8s ingress level.
N.B. on config management
Be aware that having this kind of fluent upgrades (blue/green) can be made much worse if you do naive management of ConfigMaps. At the very least, you must have ConfigMaps in git repos as well (not Secrets, of course). Optimally, you use something like kustomize's content-hash-based ConfigMap names. This means you have no issues keeping multiple versions of ConfigMaps around along with the deployments needing them, and if you switch back and forth between your blue/green deployments, each of them keeps their individual versions of the same ConfigMap.
N.B. on circular dependencies
Just don't do it. You don't want to have two services which depend on each other, or several services which form a circle and can only be deployed in one feel swoop - especially not crossing cluster boundaries. If you find something like this, do yourself a favor and break the circle before you do anything else.