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:

  1. 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.
  2. 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?

  • kersplort@programming.dev
    link
    fedilink
    arrow-up
    1
    ·
    edit-2
    1 year ago

    Get good at the three point turn.

    • Add the new code path/behavior. Release - this can be a minor version in semver.
    • Mark the old code path or behavior as deprecated. Release - this can be another minor version.
      • In between here, clean up any dependencies or give your users time to clean up.
    • Remove the old code path or behavior. Release. If you’re using semver, this is the major version change.

    This is a stable way to make changes on any system that has a dependency on another platform, repository, or system. It’s good practice for anything on the web, as users may have logged in or long running sessions, and it works for systems that call each other and get released on different cadences.

  • Kissaki@programming.dev
    link
    fedilink
    English
    arrow-up
    1
    ·
    edit-2
    5 months ago

    I don’t have multi-user library maintenance experience in particular, but

    I think a library with multiple users has to have a particular consideration for them.

    1. Make changes in a well-documented and obvious way
      1. Each release has a list of categorized changes (and if the lib has multiple concerns or sections, preferably sectioned by them too)
      2. Each release follows semantic versioning - break existing APIs (specifically obsoletion) only on major
      3. Preferably mark obsoletion one feature or major release before a removal release
      4. Consider timing of feature / major version releases so there’s plannable time frames for users
    2. For internal company use, I would consider users close and small-number enough to think about direct feedback channels of needs and concerns and upgrade support (and maybe even pushing for them [at times])

    I think “keeping all users in sync” is a hard ask that will likely cause conflict and frustration (on both sides). I don’t know your company or project landscape though. Just as a general, most common expectation.

    So between your two alternatives, I guess it’s more of point 1? I don’t think it should be “rapidly develop” though. I’m more thinking doing mindful “isolated” lib development with feedback channels, somewhat predictable planning, and documented release/upgrade changes.

    If you’re not doing mindful thorough release management, the “saved” effort will likely land elsewhere, and may very well be much higher.