How do you balance rapid iteration and merging/upgrading?
More specifically, I'm thinking about two different modes of development for a library (private to the company) that's already relied upon by other libraries and applications:
Rapidly develop the library "in isolation" without being slowed down by keeping all of the users in sync. This causes more divergence and merge effort the longer you wait to upgrade users.
Make all changes in lock-step with users, keeping everyone in sync for every change that is made. This will be slower and might result in wasted work if experimental changes are not successful.
As a side note: I believe these approaches are similar in spirit to the continuum of microservices vs monoliths.
Speaking from recent experience, I feel like I'm repeatedly finding that users of my library have built towers upon obsolete APIs, because there have been multiple phases of experimentation that necessitated large changes. So with each change, large amounts of code need to be rewritten.
I still think that approach #1 was justified during the early stages of the project, since I wanted to identify all of the design problems as quickly as possible through iteration. But as the API is getting closer to stabilization, I think I need to switch to mode #2.
How do you know when is the right time to switch? Are there any good strategies for avoiding painful upgrades?
One approach I’ve seen (from a user pov, not dev, so I’ve no idea of the code bloat it might cause) is to pass the API version number in the call. Then your api can be backwards compatible for 2 or 3 versions, giving other users time to upgrade their code. It de-couples things to give you all a bit of slack for both rapid iteration and stability.
But it also depends on the ‘contract’ between you and the users so be very clear how long / how many versions will be available. Probably will involve a ‘use by’ date.
I don't understand. If you are able to upgrade by just passing a new version number to an API, then the API signature hasn't really changed enough to necessitate any changes on behalf of the user, right? Like, the API function signature hasn't changed?
The kinds of rapid iteration I'm talking about might involve completely removing APIs and making large breaking changes to the API surface itself, requiring user code to be rewritten to some degree.
We’ll of course it depends on the scale of the changes. Depending on how your calling them, the version could be in the url, such as zooms api including /v2/ in the urls. Then you can introduce /v3 with many changes whilst leaving /v2 in place for some amount of time.
If /v3 also means a complete change of database and other underlying infrastructure (eg removing the concept of a zoom meeting), then you’ve got different challenges. Those are probably about overall design, not api.
The idea doesn't change if it's rest or a code library. The version definition would just go in you requirements.txt or go.mod or whatever instead of a url endpoint.