What I've learned about API versioning:

1. Versioning an entire web service or RPC API is relatively expensive in terms of engineering resources. Wherever possible, avoid having more than one version of an API, in favor of a single API version that evolves gradually in backwards-compatible ways. Having multiple API versions is a recourse to employ when you've realized that the original API isn't ideal, and you need to break compatibility to get it right. Strive to avoid this.

2. Establish ground rules with your clients about what kind of changes will be considered backward-compatible: what kind of changes they are expected to tolerate. For example, adding new fields to existing structures is something that clients should tolerate from the service. Pay careful attention to enumerations, sum types, and exceptions.

3. Version upgrades are expensive. Upgrading from one API version to another typically requires engineering time from every single client application. Any time that clients spend on version upgrades is time taken away from more useful activities. The total cost of a version upgrade grows in proportion to the number of clients. Don't gratuitously release new API major versions; strive to minimize them.

4. As a service provider, it often seems convenient to just release a new API version rather than put in the hard work to provide a feature in a backward-compatible way. (I see this more often in libraries than in services, where it's difficult to know 100% for sure that your change to the library won't break anyone.) Resist this temptation. Just because the upgrade costs your clients pay are hidden from you doesn't mean they don't exist. Version upgrades are disruptive and frustrating.

5. It's sometimes possible to implement backwards-incompatible changes through a series of smaller backwards-compatible or non-disruptive changes. For example, say that you want to rename an API element -- normally this would be a backward-incompatible change. However, you can break the change up into multiple steps: first, add the new element with the desired name and announce that the old name is deprecated. Next, work with your clients to upgrade them all to the new name. Once clients have migrated, you can remove the old name without impact. (How hard this is and how long it takes depends on the situation; it varies from relatively easy to almost impossible. A key factor is your clients - the more you can coordinate with and influence your clients, the easier this is.)

6. Releasing changes in backward-compatible or non-impacting ways is especially useful because it reduces the need for a service provider and client to directly coordinate. The service provider can announce a feature or change and clients can update to account for it on their own timeline. The service provider can then implement any backward-incompatible steps once all clients are ready.

7. When running any kind of migration, such as renaming an element or moving clients from one major version to another, it's helpful to have statistics about what people are using. If your V1 API isn't getting any traffic because users have moved to V2, or alternatively if it's continuing to receive traffic, then that's important to know if you're planning to deprecate it.

8. Versioning can usually be added after-the-fact if it's needed. It's often not necessary to worry about it too much up-front. I have yet to run into a situation where the lack of a version field caused a problem (because its absence can be interpreted as Version=1).

9. Most of what I said above matters when you have a large number of clients, or when you have clients that are outside your influence. This is big company, big-usage thinking. If you're e.g. a small company building services for your own consumption, don't worry about API versioning until you begin to notice friction. Instead, focus on making changes in backwards-compatible ways. If you need to make a backward-incompatible change, then talk to the people whose software will be affected. There might be an easier way to manage the change than maintaining two parallel API versions.