summaryrefslogtreecommitdiff
path: root/doc/dev/semver_status.md
blob: fef81bfdce27893116718ec268bb007c5dd880ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# Semver tracking

We no longer use this file for semver tracking.  Instead, we use one
`semver.md` file per crate.

When you make a change to a crate that affects source compatibility,
please append a paragraph to that crate's `semver.md`, creating it as
necessary.

Every line should begin with one of the following:
  * BREAKING
  * ADDED

A "BREAKING" change is one that may break other crates that depend on
this crate directly.

An "ADDED" change is one the introduces a new API, such that crates
using the new API will not work with older versions of the crate.

When we release a new version, we use these files to determine which
crates need major-version, minor-version, or patch-level version bumps.
We also use them to help write the the changelog,
particularly the "breaking changes" section.
They aren't user-facing, and will be reworked by the changelog author,
so they don't go into much detail,

If a single API changes in multiple ways that are both BREAKING and ADDED,
it is sufficient to write just a BREAKING entry.

Here is an example `semver.md` file:

>```
>BREAKING: Removed the obsolete `detect_thylacine()` function.
>
>ADDED: New `Wombat::feed()` method.
>
>ADDED: `Numbat` now implements `Display`.
>
>BREAKING: The `Quokka` trait now inherits from Debug.
>```

## What is a breaking change?

We will add guidance to this section as we come up with it. For now, see
[SemVer compatibility] in the Cargo book.

[SemVer Compatibility]: https://doc.rust-lang.org/cargo/reference/semver.html

### When types from lower-level crates appear in the APIs of higher-level crates

If a type (concrete type or trait) from a lower-level crate
is returned (or accepted)
by an API of a higher-level crate,
then a breaking change to that lower-level crate is a breaking change
for the higher-level crate, too.

This includes any case where a higher-layer type
implements a public trait from a lower-layer;
even if the type is not itself re-exported by the higher-layer crate.

#### Reasoning, and worked example:

Suppose `tor_error::ErrorKind` gets a breaking change,
and we bump the major[1] for `tor_error` but not for `arti_client`.
Obviously our new `arti_client` uses the new `tor_error`.

A downstream might use both `tor-error` and `arti-client`.
If they do this in the usual way,
a `cargo update` will get them the new `arti_client` but old `tor_error`.
Now their program has *two* instances of `tor_error`:
one whose `ErrorKind` is implemented by `arti_client::Error`,
and one that the downstream application code sees.
The effect is that the downstream application
can no longer call `.has_kind()` on an `arti_client::Error`
because they don't have the *right* `HasKind` method in scope.

Note that this does not depend on the nature of the breaking change,
nor on the re-export of any names.
It only depends on the exposure of the type and its trait implementations.

(The ["semver trick"](https://github.com/dtolnay/semver-trick)
can sometimes be used to help multiple different versions
of the same crate share global state, or perhaps, traits etc.)

[1] "Major" here includes the 2nd component in a 0.x version.