RIOT-OS / RIOT

RIOT - The friendly OS for IoT
https://riot-os.org
GNU Lesser General Public License v2.1
4.97k stars 1.99k forks source link

[RFC] Versioning our user facing APIs #8561

Closed miri64 closed 5 years ago

miri64 commented 6 years ago

Motivation

We discussed @ FU Berlin a while back offline that it might be a good idea to version some of our user facing APIs to make communication of changes in the API more obvious beyond just the RIOT release. And since I smell some major API reworks coming again (#8351, a sock rework @kaspar030 was proposing in #8149, ...) I think it is about time to pick this discussion up again.

What are user facing APIs

Well... that needs to be discussed... I think that versioning all header files would be overkill. When they are identified they should be collected on a special page in the documentation for any user easy to find (which we should do anyway for new users, regardless of versioning) including their current version. From the top of my head I would include definitively and in no particular order:

How to version

Notation

In doxygen there is the @version tag which I think we can use. For example sock would be versioned in sys/include/net/sock.h

/**
 * @defgroup    net_sock    Sock API
 * @ingroup     net
 * @brief       Provides a network API for applications and library
 * @version     v1.1.0      Asynchronous event management added
 * @version     v1.0.0      Initial sock version
 * @version     v0.1.0      Initial conn version
 * ...

Maybe even add the commit hash that changed the API to it as well.

Scheme

As to what scheme to use for versioning, semantic versioning was tossed around by @cladmi, @kYc0o and @aabadie: The version is a triple consisting of 3 numbers

  1. Major version: increments (and resets 2 and 3 to 0) whenever an API change makes it incompatible with software that uses an older version of the API.
  2. Minor version: increment (and resets 3) whenever an API change adds functionality in a backwards-compatible manner.
  3. Patch version: increments whenever there is a backwards-compatible bug-fix to the API (e.g. making a pointer in a function parameter const, moving from a specific pointer to a generic void * pointer etc.).

API deprecation

With this process we could also add a BCP for API deprecation:

kaspar030 commented 6 years ago

it might be a good idea to version some of our user facing APIs to make communication of changes in the API more obvious beyond just the RIOT release.

Which specific problem are you trying to solve?

In doxygen there is the @version tag which I think we can use.

How would we use that to automatically notify a user?

The version is a triple consisting of 3 numbers

The linked semantic versioning scheme is supposed to be used for implementations ("software", "packages"), not just an API. API only gets two numbers.

miri64 commented 6 years ago

Which specific problem are you trying to solve?

People complaining about RIOT not being stable enough for their purposes / not being clear enough about API changes.

How would we use that to automatically notify a user?

Why would an automatic notification be required? I know of no software platform that does that. This issue is about documenting API changes.

The linked semantic versioning scheme is supposed to be used for implementations ("software", "packages"), not just an API. API only gets two numbers.

Where do you get this from? The linked document talks a lot about "public API[s]". Are you referring to comments made during that one meeting where we discussed this? If not, please provide a source. Regarding the comments in the meeting: I'm of a different opinion today. The fixes I exemplified for (3) show that bug fixes are also something that exists in the scope of API, though they only might have a far minor impact. Other such "bug fixes" (or let's call them changes that smaller than major or minor) would also be to document previously insufficiently documented return values (e.g. @returns -EINVAL when @p x is invalid instead of @returns negative value on error).

kaspar030 commented 6 years ago

People complaining about RIOT not being stable enough for their purposes / not being clear enough about API changes.

Why would an automatic notification be required?

Well, for being "clear" about changes, documentation might be enough in order to later tell the unhappy people "look, the change was documented".

RIOT's API not being stable is not fixed by versioning the API.

Some tool telling users what changes affects their code, and a hint on how to fix, might help. Just asking.

I know of no software platform that does that.

Well, your linux box uses semantic versioning to dynamically link the correct library binaries. At least cargo (from rust) uses semantic versioning for dependency checking.

Where do you get this from?

"Bug fixes not affecting the API increment the patch version" -> something that is not the API can be changed -> triple is for software, not the API.

Please don't get offended, I'm totally pro API versioning. Just trying to clarify things...

miri64 commented 6 years ago

Well, for being "clear" about changes, documentation might be enough in order to later tell the unhappy people "look, the change was documented".

RIOT's API not being stable is not fixed by versioning the API.

True, but it makes changes more visible and easier to track: "Oh minor version change, well than I can be relieved, let's see what nice things they added" vs. "Mh major version change, let's see what changed so I can adapt". Hopefully major version steps become a rarer thing once we need to document the changes (developers hate documentation after all ;-)).

Some tool telling users what changes affects their code, and a hint on how to fix, might help. Just asking.

Well, the doc demon could also watch for the version tag, and whenever one is added, write a mail to users, generate a blog post at riot-os.org, etc. This might be even sensible to do anyway, so we can keep documentation on older versions of an API at doc.riot-os.org (e.g. http://doc.riot-os.org/group__drivers__netdev__api__v1.1.0.html). But this goes into details that might come to importants after we decided on this issue.

I know of no software platform that does that.

Well, your linux box uses semantic versioning to dynamically link the correct library binaries. At least cargo (from rust) uses semantic versioning for dependency checking.

I don't know what this has to do with user notification (which my comment was about). I know however, that semantic versioning is wide spread and well-used, that's why I proposed it ;-).

"Bug fixes not affecting the API increment the patch version" -> something that is not the API can be changed -> triple is for software, not the API.

Maaybee just maaaybe the third component is overkill. However, I'd like to keep it on the table for now.

Please don't get offended, I'm totally pro API versioning. Just trying to clarify things...

No offense taken. Sorry for sounding more upset than I actually am ;-). I appreciate the input.

kaspar030 commented 6 years ago

True, but it makes changes more visible and easier to track

I'm not so sure about that. These are the includes of a random real-world riot application:

#include "periph/gpio.h"
#include "periph/rtc.h"
#include "xtimer.h"
#include "mutex.h"
#include "gsm.h"
#include "gps/serial.h"
#include "log.h"

#include "rtc_tim.h"

#include "mma8x5x.h"
#include "mma8x5x_params.h"

#include "jsmn.h"
#include "json.h"

#include "fmt.h"
#include "pm_layered.h"

I think almost all of the headers can be considered "user facing".

There's no chance in hell that any API change in those headers won't be recognized by the compiler first (should they be incompatible), compared to the user checking the versions of the used modules in doxygen and realizing "oh, major change, I need to adapt".

Worse, a documented change in the semantics (increasing maybe even the major version), which doesn't change the actual API header in an incompatible (to the compiler) way, will just stay unnoticed until the application misbehaves.

IMO, something automatic nagging the user at compile time, pointing to "how-to-upgrade-your-code" docs, should be an important part of any versioning scheme that we come up with.

miri64 commented 6 years ago

I think almost all of the headers can be considered "user facing".

Many I'm not really agreeing with. In particular:

#include "gsm.h"           // netdev?
#include "gps/serial.h"    // saul?
#include "rtc_tim.h"       // periph/rtc or POSIX gettimeofday?
#include "mma8x5x.h"       // both saul?
#include "mma8x5x_params.h"
#include "jsmn.h"          // isn't this external?

IMO, something automatic nagging the user at compile time, pointing to "how-to-upgrade-your-code" docs, should be an important part of any versioning scheme that we come up with.

I think if we define how to version APIs first we can then talk about how to connect this to module definitions. But some thoughts from a high-level perspective: We could do it like most package management softwares (apt, pip, etc) do it by having module meaning just the newest version, module>=x.y meaning any version equal or greater than x.y, module<=x.y any version equal or lesser than x.y and so on. This has however two disadvantages:

  1. Users are lazy, they most likely will say module without any version specification until they run into the problems you mentioned. Than any automation is null and void and it backtraces to problem of the user having to check the doxygen (or worse: opening an issue) you mentioned
  2. If we enforce having at least the last version developed against to be specified to prevent (1), this might get tedious, especially for very stable APIs => people will develop workarounds => possibility to end-up in (1).
miri64 commented 6 years ago

(just musings of a person often implementing against python setuptools ;-))

kaspar030 commented 6 years ago

Many I'm not really agreeing with. In particular:

Does that matter? The point is, there'll be either many versioned API's, or not enough, or just one, (or none, which would be our current state).

With "not enough" I would describe a state where maybe the core/ headers and sock are versioned and most other sys/ modules are not, making versioning's use quite limited. With "just one" I mean the possiblilty of versioning the whole RIOT source tree with just one version. (Don't think that would be useful, either.)

I think if we define how to version APIs first we can then talk about how to connect this to module definitions.

Well, it might help to think about who'll be using / reading the versioning scheme, before agreeing on a way.

miri64 commented 6 years ago

Does that matter? The point is, there'll be either many versioned API's, or not enough, or just one, (or none, which would be our current state).

With "not enough" I would describe a state where maybe the core/ headers and sock are versioned and most other sys/ modules are not, making versioning's use quite limited. With "just one" I mean the possiblilty of versioning the whole RIOT source tree with just one version. (Don't think that would be useful, either.)

Well, I opened this issue to potentially also discuss which APIs should be versioned. But maybe we should focus on the how first ;-). However, I think there is a sweet spot (or even sweet zone) between overkill and not enough / just one ;-). Tossing ideas around: maybe a user survey could offer some answers here.

Well, it might help to think about who'll be using / reading the versioning scheme, before agreeing on a way.

Granted.

rfuentess commented 6 years ago

Well, I opened this issue to potentially also discuss which APIs should be versioned.

My two cents: gcoap and nanocoap.

And maybe this is redundant (or overkill), but any API making use of external packages.

jia200x commented 6 years ago

+1 to Semantic ~Releases~ schema, it's widely adopted and well known.

Does it make sense to:

  1. Use tools like semantic-release for automatically updating Major, Minor and Patch and
  2. Use the generated CHANGELOG + Doxygen to inform about API changes?

The hardest part of 1. would be to agree in a commit message format. From semantic-release:

By default semantic-release uses Angular Commit Message Conventions. The commit message format can be changed with the preset or config options of the @semantic-release/commit-analyzer and @semantic-release/release-notes-generator plugins.

Tools such as commitizen, commitlint or semantic-git-commit-cli can be used to help contributors and enforce valid commit messages.

The rest comes almost our of the box :)

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions.