github.com/number571/tendermint@v0.34.11-gost/docs/architecture/adr-060-go-api-stability.md (about) 1 # ADR 060: Go API Stability 2 3 ## Changelog 4 5 - 2020-09-08: Initial version. (@erikgrinaker) 6 7 - 2020-09-09: Tweak accepted changes, add initial public API packages, add consequences. (@erikgrinaker) 8 9 - 2020-09-17: Clarify initial public API. (@erikgrinaker) 10 11 ## Context 12 13 With the release of Tendermint 1.0 we will adopt [semantic versioning](https://semver.org). One major implication is a guarantee that we will not make backwards-incompatible changes until Tendermint 2.0 (except in pre-release versions). In order to provide this guarantee for our Go API, we must clearly define which of our APIs are public, and what changes are considered backwards-compatible. 14 15 Currently, we list packages that we consider public in our [README](https://github.com/number571/tendermint#versioning), but since we are still at version 0.x we do not provide any backwards compatiblity guarantees at all. 16 17 ### Glossary 18 19 * **External project:** a different Git/VCS repository or code base. 20 21 * **External package:** a different Go package, can be a child or sibling package in the same project. 22 23 * **Internal code:** code not intended for use in external projects. 24 25 * **Internal directory:** code under `internal/` which cannot be imported in external projects. 26 27 * **Exported:** a Go identifier starting with an uppercase letter, which can therefore be accessed by an external package. 28 29 * **Private:** a Go identifier starting with a lowercase letter, which therefore cannot be accessed by an external package unless via an exported field, variable, or function/method return value. 30 31 * **Public API:** any Go identifier that can be imported or accessed by an external project, except test code in `_test.go` files. 32 33 * **Private API:** any Go identifier that is not accessible via a public API, including all code in the internal directory. 34 35 ## Alternative Approaches 36 37 - Split all public APIs out to separate Go modules in separate Git repositories, and consider all Tendermint code internal and not subject to API backwards compatibility at all. This was rejected, since it has been attempted by the Tendermint project earlier, resulting in too much dependency management overhead. 38 39 - Simply document which APIs are public and which are private. This is the current approach, but users should not be expected to self-enforce this, the documentation is not always up-to-date, and external projects will often end up depending on internal code anyway. 40 41 ## Decision 42 43 From Tendermint 1.0, all internal code (except private APIs) will be placed in a root-level [`internal` directory](https://golang.org/cmd/go/#hdr-Internal_Directories), which the Go compiler will block for use by external projects. All exported items outside of the `internal` directory are considered a public API and subject to backwards compatibility guarantees, except files ending in `_test.go`. 44 45 The `crypto` package may be split out to a separate module in a separate repo. This is the main general-purpose package used by external projects, and is the only Tendermint dependency in e.g. IAVL which can cause some problems for projects depending on both IAVL and Tendermint. This will be decided after further discussion. 46 47 The `tm-db` package will remain a separate module in a separate repo. The `crypto` package may possibly be split out, pending further discussion, as this is the main general-purpose package used by other projects. 48 49 ## Detailed Design 50 51 ### Public API 52 53 When preparing our public API for 1.0, we should keep these principles in mind: 54 55 - Limit the number of public APIs that we start out with - we can always add new APIs later, but we can't change or remove APIs once they're made public. 56 57 - Before an API is made public, do a thorough review of the API to make sure it covers any future needs, can accomodate expected changes, and follows good API design practices. 58 59 The following is the minimum set of public APIs that will be included in 1.0, in some form: 60 61 - `abci` 62 - packages used for constructing nodes `config`, `libs/log`, and `version` 63 - Client APIs, i.e. `rpc/client`, `light`, and `privval`. 64 - `crypto` (possibly as a separate repo) 65 66 We may offer additional APIs as well, following further discussions internally and with other stakeholders. However, public APIs for providing custom components (e.g. reactors and mempools) are not planned for 1.0, but may be added in a later 1.x version if this is something we want to offer. 67 68 For comparison, the following are the number of Tendermint imports in the Cosmos SDK (excluding tests), which should be mostly satisfied by the planned APIs. 69 70 ``` 71 1 github.com/number571/tendermint/abci/server 72 73 github.com/number571/tendermint/abci/types 73 2 github.com/number571/tendermint/cmd/tendermint/commands 74 7 github.com/number571/tendermint/config 75 68 github.com/number571/tendermint/crypto 76 1 github.com/number571/tendermint/crypto/armor 77 10 github.com/number571/tendermint/crypto/ed25519 78 2 github.com/number571/tendermint/crypto/encoding 79 3 github.com/number571/tendermint/crypto/merkle 80 3 github.com/number571/tendermint/crypto/sr25519 81 8 github.com/number571/tendermint/crypto/tmhash 82 1 github.com/number571/tendermint/crypto/xsalsa20symmetric 83 11 github.com/number571/tendermint/libs/bytes 84 2 github.com/number571/tendermint/libs/bytes.HexBytes 85 15 github.com/number571/tendermint/libs/cli 86 2 github.com/number571/tendermint/libs/cli/flags 87 2 github.com/number571/tendermint/libs/json 88 30 github.com/number571/tendermint/libs/log 89 1 github.com/number571/tendermint/libs/math 90 11 github.com/number571/tendermint/libs/os 91 4 github.com/number571/tendermint/libs/rand 92 1 github.com/number571/tendermint/libs/strings 93 5 github.com/number571/tendermint/light 94 1 github.com/number571/tendermint/internal/mempool 95 3 github.com/number571/tendermint/node 96 5 github.com/number571/tendermint/internal/p2p 97 4 github.com/number571/tendermint/privval 98 10 github.com/number571/tendermint/proto/tendermint/crypto 99 1 github.com/number571/tendermint/proto/tendermint/libs/bits 100 24 github.com/number571/tendermint/proto/tendermint/types 101 3 github.com/number571/tendermint/proto/tendermint/version 102 2 github.com/number571/tendermint/proxy 103 3 github.com/number571/tendermint/rpc/client 104 1 github.com/number571/tendermint/rpc/client/http 105 2 github.com/number571/tendermint/rpc/client/local 106 3 github.com/number571/tendermint/rpc/core/types 107 1 github.com/number571/tendermint/rpc/jsonrpc/server 108 33 github.com/number571/tendermint/types 109 2 github.com/number571/tendermint/types/time 110 1 github.com/number571/tendermint/version 111 ``` 112 113 ### Backwards-Compatible Changes 114 115 In Go, [almost all API changes are backwards-incompatible](https://blog.golang.org/module-compatibility) and thus exported items in public APIs generally cannot be changed until Tendermint 2.0. The only backwards-compatible changes we can make to public APIs are: 116 117 - Adding a package. 118 119 - Adding a new identifier to the package scope (e.g. const, var, func, struct, interface, etc.). 120 121 - Adding a new method to a struct. 122 123 - Adding a new field to a struct, if the zero-value preserves any old behavior. 124 125 - Changing the order of fields in a struct. 126 127 - Adding a variadic parameter to a named function or struct method, if the function type itself is not assignable in any public APIs (e.g. a callback). 128 129 - Adding a new method to an interface, or a variadic parameter to an interface method, _if the interface already has a private method_ (which prevents external packages from implementing it). 130 131 - Widening a numeric type as long as it is a named type (e.g. `type Number int32` can change to `int64`, but not `int8` or `uint32`). 132 133 Note that public APIs can expose private types (e.g. via an exported variable, field, or function/method return value), in which case the exported fields and methods on these private types are also part of the public API and covered by its backwards compatiblity guarantees. In general, private types should never be accessible via public APIs unless wrapped in an exported interface. 134 135 Also note that if we accept, return, export, or embed types from a dependency, we assume the backwards compatibility responsibility for that dependency, and must make sure any dependency upgrades comply with the above constraints. 136 137 We should run CI linters for minor version branches to enforce this, e.g. [apidiff](https://go.googlesource.com/exp/+/refs/heads/master/apidiff/README.md), [breakcheck](https://github.com/gbbr/breakcheck), and [apicombat](https://github.com/bradleyfalzon/apicompat). 138 139 #### Accepted Breakage 140 141 The above changes can still break programs in a few ways - these are _not_ considered backwards-incompatible changes, and users are advised to avoid this usage: 142 143 - If a program uses unkeyed struct literals (e.g. `Foo{"bar", "baz"}`) and we add fields or change the field order, the program will no longer compile or may have logic errors. 144 145 - If a program embeds two structs in a struct, and we add a new field or method to an embedded Tendermint struct which also exists in the other embedded struct, the program will no longer compile. 146 147 - If a program compares two structs (e.g. with `==`), and we add a new field of an incomparable type (slice, map, func, or struct that contains these) to a Tendermint struct which is compared, the program will no longer compile. 148 149 - If a program assigns a Tendermint function to an identifier, and we add a variadic parameter to the function signature, the program will no longer compile. 150 151 ### Strategies for API Evolution 152 153 The API guarantees above can be fairly constraining, but are unavoidable given the Go language design. The following tricks can be employed where appropriate to allow us to make changes to the API: 154 155 - We can add a new function or method with a different name that takes additional parameters, and have the old function call the new one. 156 157 - Functions and methods can take an options struct instead of separate parameters, to allow adding new options - this is particularly suitable for functions that take many parameters and are expected to be extended, and especially for interfaces where we cannot add new methods with different parameters at all. 158 159 - Interfaces can include a private method, e.g. `interface { private() }`, to make them unimplementable by external packages and thus allow us to add new methods to the interface without breaking other programs. Of course, this can't be used for interfaces that should be implementable externally. 160 161 - We can use [interface upgrades](https://avtok.com/2014/11/05/interface-upgrades.html) to allow implementers of an existing interface to also implement a new interface, as long as the old interface can still be used - e.g. the new interface `BetterReader` may have a method `ReadBetter()`, and a function that takes a `Reader` interface as an input can check if the implementer also implements `BetterReader` and in that case call `ReadBetter()` instead of `Read()`. 162 163 ## Status 164 165 Accepted 166 167 ## Consequences 168 169 ### Positive 170 171 - Users can safely upgrade with less fear of applications breaking, and know whether an upgrade only includes bug fixes or also functional enhancements 172 173 - External developers have a predictable and well-defined API to build on that will be supported for some time 174 175 - Less synchronization between teams, since there is a clearer contract and timeline for changes and they happen less frequently 176 177 - More documentation will remain accurate, since it's not chasing a moving target 178 179 - Less time will be spent on code churn and more time spent on functional improvements, both for the community and for our teams 180 181 ### Negative 182 183 - Many improvements, changes, and bug fixes will have to be postponed until the next major version, possibly for a year or more 184 185 - The pace of development will slow down, since we must work within the existing API constraints, and spend more time planning public APIs 186 187 - External developers may lose access to some currently exported APIs and functionality 188 189 ## References 190 191 - [#4451: Place internal APIs under internal package](https://github.com/number571/tendermint/issues/4451) 192 193 - [On Pluggability](https://docs.google.com/document/d/1G08LnwSyb6BAuCVSMF3EKn47CGdhZ5wPZYJQr4-bw58/edit?ts=5f609f11)