语义版本控制对参数名称更改意味着什么?

What does semantic versioning imply about parameter name changes?

试图向朋友解释语义版本控制的重要性时,我遇到了以下困境。

假设我们有库 libfoo,版本 1.2.3 公开了以下函数:

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

现在假设此函数及其文档更改为:

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

我的第一印象是下一个版本会是1.2.4,因为public界面没有改变。例如,像这样调用函数的人根本不会注意到变化:

foo(3, 4)

但进一步思考,这很可能是一个 API 中断 ,因为 Python 允许通过名称指定参数。如果有人像这样调用我的函数:

foo(y=4, x=3)

这将不再适用于版本 1.2.4,违反了语义版本控制契约。

另一方面,这样的变化似乎很小,以至于我不希望将版本增加到 2.0.0

总而言之,这是否构成 API 中断?在这种情况下,下一个版本号应该是什么?

此类更改可能属于发布规模的许多不同领域。

重大变化(从 1.x 增加到 2.x)

这违反了您的 API 合同,属于重大变更。不过,需要注意的是,这是否是唯一的变化。如果是这样,我不会将其作为主要版本更改。另一方面,如果这是也破坏您的 API 合同的众多更改之一,我相信主要版本增量是合理的。

小改动(从 1.2 增加到 1.3)

借用 Python documentation:

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

对我来说,这是一个小改动。正如您所说,如果用户没有命名他们的参数,他们甚至不会注意到发生了变化。

微小变化(从 1.2.3 增加到 1.2.4)

这是您的错误修复级别的更改。如果您将 foo(x, y) 更改为 foo(a, b) 因为它是一个错误,那么此修复证明微点增量是合理的。


我对此类更改的看法是使其成为 次要 更改。这当然不是 "earth shattering" 更改,但它确实可能会强制您的最终用户更改代码。我不会将其归类为主要更改,因为它仅在函数调用中更改参数名称,否则函数调用的行为完全相同,returns 完全相同的数据,并采用完全相同的数据作为参数,但仅适用它们在函数中的名称不同。

简短回答:是的,我认为 构成 API 中断,因此可能会增加主版本号。不过请注意以下注意事项。


当您公开 public/external API 时,您承担了额外的 “注意义务” 以仔细考虑界面的更改。这包括,例如,推迟潜在的改进以避免破坏向后兼容性*。 任何会影响任何代码合法**使用您的界面的任何更改在维护API时都应该非常仔细地考虑。

specification for semantic versioning是明确的:

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

正如您在问题中指出的那样,更改参数名称会导致通过关键字传递参数的代码向后不兼容。

但是,与其说这个更改应该增加主要版本,不如说 不应该进行更改,或者至少不应该单独进行 - 它太微小的变化以证明可能破坏现有有效代码的主要增量是合理的。除了以下任一情况:

  1. 这是一些更大的重要变化的一部分;或
  2. 有一个 确实 没有在您的示例中显示的更改的充分理由(一​​些停止显示的错误,或依赖它的其他功能);

我会完全推迟更改。最好放慢速度并确保继续满足语义版本控制合同,只有在有充分理由的情况下才能进行此类更改。


来自Python 3.8 you can specify positional-only parameters, with exactly this kind of issue being called out in the PEP 作为理由的一部分(强调我的):

If a caller of an API starts using a keyword argument, the library author cannot rename the parameter because it would be a breaking change.

在这种情况下,如果原始定义是:

def foo(x, y, /):

然后重命名参数将不会是一个重大变化。


* 因为我们在 Python 标签中,请考虑整数除法,尽管 being acknowledged as a mistake by the BDFL,至今仍保留在 2.x 版本中。

** 我说“合法”是为了排除未按照官方文档使用它的代码,例如通过访问按约定私有的属性——他们应该预料到有时会感到不便!因此,如果您预测到此更改并且明确指定仅应使用位置参数,则此更改是可以的,但那将是一个奇怪的选择。