What does semantic versioning imply about paramete

2019-04-08 13:52发布

问题:

Trying to explain the importance of semantic versioning to a friend, I faced the following dilemma.

Let's say we have library libfoo, version 1.2.3 that exposes the following function:

def foo(x, y):
    """
    Compute the sum of the operands.
    :param x: The first argument.
    :param y: The second argument.
    :returns: The sum of `x` and `y`.
    """
    return x + y

Now assume that this function and its documentation change to:

def foo(a, b):
    """
    Compute the sum of the operands.
    :param a: The first argument.
    :param b: The second argument.
    :returns: The sum of `a` and `b`.
    """
    return a + b

My first impression was to say that the next version would be 1.2.4, as the public interface did not change. For example, someone calling the function like that would not notice the change at all:

foo(3, 4)

But thinking a bit further, this may very well be an API break, given that Python allows one to specify parameters by their names. If someone were to call my function like:

foo(y=4, x=3)

this would not work any more with the version 1.2.4, breaking the semantic versioning contract.

On the other hand, such a change seems so small that I would feel bad about increasing the version to 2.0.0.

To summarize, does this constitute an API break? What should the next version number be in this case?

回答1:

Short answer: yes, I think that would constitute an API break and thus potentially increment the major version number. Note the caveats below, though.


When you expose a public/external API, you take on an extra "duty of care" to think carefully about changes to the interface. This includes, for example, putting off a potential improvement to avoid breaking backwards compatibility*. Any change that would affect any code legitimately** using your interface should be considered very carefully when you're maintaining an API.

The specification for semantic versioning is unambiguous:

Major version X (X.y.z | X > 0) MUST be incremented if any backwards incompatible changes are introduced to the public API.

Changing the names of parameters, as you have identified in the question, introduces a backwards incompatibility for code passing arguments by keyword.

However, rather than say that this change should increment the major version, I would conclude instead that the change should not be made, or at least not in isolation - it's too minor a change to justify the major increment that potentially breaking existing valid code would entail. Except for the case that either:

  1. It's part of some larger bundle of important changes; or
  2. There is a really good reason for the change that isn't shown in your example (some show-stopping bug, or other feature that relies on it);

I would put off the change altogether. It's better to move more slowly and ensure that you continue to meet your semantic versioning contract, making such changes only with compelling grounds to do so.

* As we're in the Python tag, consider integer division, that, despite being acknowledged as a mistake by the BDFL, remains in 2.x releases to this day.

** I say "legitimately" to exclude code not using it as officially documented, for example by accessing private-by-convention attributes - they should expect to get inconvenienced on occasion! Therefore if you'd predicted this change and it was clearly specified that only positional arguments should be used, this change would be OK, but that would be an odd choice.



回答2:

This type of change could fall into many different areas on the release scale.

Major Change (Increment from 1.x to 2.x)

This breaks your API contract and would qualify as a major change. The massive caveat to that, though, is whether or not this is the only change. If it is, I would not make this a major version change. If, on the other hand, this is one of many changes that also break your API contract, I believe a Major version increment is justified.

Minor Change (Increment from 1.2 to 1.3)

Borrowing from the Python documentation:

the minor version number [is] incremented for less earth-shattering changes.

To me, this is a minor change. As you state, if the users aren't naming their parameters, they don't even notice the change has occurred.

Micro Change (Increment from 1.2.3 to 1.2.4)

This is your bug fix level of changes. If you are changing foo(x, y) to foo(a, b) because it is a bug, then this fix justifies a micro point increment.


My opinion of such as change would be to make it a Minor change. It is certainly not an "earth shattering" change, but it does potentially force code changes from your end users. I would not classify it as a Major change, because it is changing parameter names only on a function call that otherwise behaves the exact same way, returns the exact same data, and takes the exact same data as parameters but just applies them to different names within the function.