github.com/cosmos/cosmos-sdk@v0.50.10/docs/architecture/adr-063-core-module-api.md (about)

     1  # ADR 063: Core Module API
     2  
     3  ## Changelog
     4  
     5  * 2022-08-18 First Draft
     6  * 2022-12-08 First Draft
     7  * 2023-01-24 Updates
     8  
     9  ## Status
    10  
    11  ACCEPTED Partially Implemented
    12  
    13  ## Abstract
    14  
    15  A new core API is proposed as a way to develop cosmos-sdk applications that will eventually replace the existing
    16  `AppModule` and `sdk.Context` frameworks a set of core services and extension interfaces. This core API aims to:
    17  
    18  * be simpler
    19  * more extensible
    20  * more stable than the current framework
    21  * enable deterministic events and queries,
    22  * support event listeners
    23  * [ADR 033: Protobuf-based Inter-Module Communication](./adr-033-protobuf-inter-module-comm.md) clients.
    24  
    25  ## Context
    26  
    27  Historically modules have exposed their functionality to the framework via the `AppModule` and `AppModuleBasic`
    28  interfaces which have the following shortcomings:
    29  
    30  * both `AppModule` and `AppModuleBasic` need to be defined and registered which is counter-intuitive
    31  * apps need to implement the full interfaces, even parts they don't need (although there are workarounds for this),
    32  * interface methods depend heavily on unstable third party dependencies, in particular Comet,
    33  * legacy required methods have littered these interfaces for far too long
    34  
    35  In order to interact with the state machine, modules have needed to do a combination of these things:
    36  
    37  * get store keys from the app
    38  * call methods on `sdk.Context` which contains more or less the full set of capability available to modules.
    39  
    40  By isolating all the state machine functionality into `sdk.Context`, the set of functionalities available to
    41  modules are tightly coupled to this type. If there are changes to upstream dependencies (such as Comet)
    42  or new functionalities are desired (such as alternate store types), the changes need impact `sdk.Context` and all
    43  consumers of it (basically all modules). Also, all modules now receive `context.Context` and need to convert these
    44  to `sdk.Context`'s with a non-ergonomic unwrapping function.
    45  
    46  Any breaking changes to these interfaces, such as ones imposed by third-party dependencies like Comet, have the
    47  side effect of forcing all modules in the ecosystem to update in lock-step. This means it is almost impossible to have
    48  a version of the module which can be run with 2 or 3 different versions of the SDK or 2 or 3 different versions of
    49  another module. This lock-step coupling slows down overall development within the ecosystem and causes updates to
    50  components to be delayed longer than they would if things were more stable and loosely coupled.
    51  
    52  ## Decision
    53  
    54  The `core` API proposes a set of core APIs that modules can rely on to interact with the state machine and expose their
    55  functionalities to it that are designed in a principled way such that:
    56  
    57  * tight coupling of dependencies and unrelated functionalities is minimized or eliminated
    58  * APIs can have long-term stability guarantees
    59  * the SDK framework is extensible in a safe and straightforward way
    60  
    61  The design principles of the core API are as follows:
    62  
    63  * everything that a module wants to interact with in the state machine is a service
    64  * all services coordinate state via `context.Context` and don't try to recreate the "bag of variables" approach of `sdk.Context`
    65  * all independent services are isolated in independent packages with minimal APIs and minimal dependencies
    66  * the core API should be minimalistic and designed for long-term support (LTS)
    67  * a "runtime" module will implement all the "core services" defined by the core API and can handle all module
    68    functionalities exposed by core extension interfaces
    69  * other non-core and/or non-LTS services can be exposed by specific versions of runtime modules or other modules 
    70  following the same design principles, this includes functionality that interacts with specific non-stable versions of
    71  third party dependencies such as Comet
    72  * the core API doesn't implement *any* functionality, it just defines types
    73  * go stable API compatibility guidelines are followed: https://go.dev/blog/module-compatibility
    74  
    75  A "runtime" module is any module which implements the core functionality of composing an ABCI app, which is currently
    76  handled by `BaseApp` and the `ModuleManager`. Runtime modules which implement the core API are *intentionally* separate
    77  from the core API in order to enable more parallel versions and forks of the runtime module than is possible with the
    78  SDK's current tightly coupled `BaseApp` design while still allowing for a high degree of composability and
    79  compatibility.
    80  
    81  Modules which are built only against the core API don't need to know anything about which version of runtime,
    82  `BaseApp` or Comet in order to be compatible. Modules from the core mainline SDK could be easily composed
    83  with a forked version of runtime with this pattern.
    84  
    85  This design is intended to enable matrices of compatible dependency versions. Ideally a given version of any module
    86  is compatible with multiple versions of the runtime module and other compatible modules. This will allow dependencies
    87  to be selectively updated based on battle-testing. More conservative projects may want to update some dependencies
    88  slower than more fast moving projects.
    89  
    90  ### Core Services
    91  
    92  The following "core services" are defined by the core API. All valid runtime module implementations should provide
    93  implementations of these services to modules via both [dependency injection](./adr-057-app-wiring.md) and
    94  manual wiring. The individual services described below are all bundled in a convenient `appmodule.Service`
    95  "bundle service" so that for simplicity modules can declare a dependency on a single service.
    96  
    97  #### Store Services
    98  
    99  Store services will be defined in the `cosmossdk.io/core/store` package.
   100  
   101  The generic `store.KVStore` interface is the same as current SDK `KVStore` interface. Store keys have been refactored
   102  into store services which, instead of expecting the context to know about stores, invert the pattern and allow
   103  retrieving a store from a generic context. There are three store services for the three types of currently supported
   104  stores - regular kv-store, memory, and transient:
   105  
   106  ```go
   107  type KVStoreService interface {
   108      OpenKVStore(context.Context) KVStore
   109  }
   110  
   111  type MemoryStoreService interface {
   112      OpenMemoryStore(context.Context) KVStore
   113  }
   114  type TransientStoreService interface {
   115      OpenTransientStore(context.Context) KVStore
   116  }
   117  ```
   118  
   119  Modules can use these services like this:
   120  
   121  ```go
   122  func (k msgServer) Send(ctx context.Context, msg *types.MsgSend) (*types.MsgSendResponse, error) {
   123      store := k.kvStoreSvc.OpenKVStore(ctx)
   124  }
   125  ```
   126  
   127  Just as with the current runtime module implementation, modules will not need to explicitly name these store keys,
   128  but rather the runtime module will choose an appropriate name for them and modules just need to request the
   129  type of store they need in their dependency injection (or manual) constructors.
   130  
   131  #### Event Service
   132  
   133  The event `Service` will be defined in the `cosmossdk.io/core/event` package.
   134  
   135  The event `Service` allows modules to emit typed and legacy untyped events:
   136  
   137  ```go
   138  package event
   139  
   140  type Service interface {
   141    // EmitProtoEvent emits events represented as a protobuf message (as described in ADR 032).
   142    //
   143    // Callers SHOULD assume that these events may be included in consensus. These events
   144    // MUST be emitted deterministically and adding, removing or changing these events SHOULD
   145    // be considered state-machine breaking.
   146    EmitProtoEvent(ctx context.Context, event protoiface.MessageV1) error
   147  
   148    // EmitKVEvent emits an event based on an event and kv-pair attributes.
   149    //
   150    // These events will not be part of consensus and adding, removing or changing these events is
   151    // not a state-machine breaking change.
   152    EmitKVEvent(ctx context.Context, eventType string, attrs ...KVEventAttribute) error
   153  
   154    // EmitProtoEventNonConsensus emits events represented as a protobuf message (as described in ADR 032), without
   155    // including it in blockchain consensus.
   156    //
   157    // These events will not be part of consensus and adding, removing or changing events is
   158    // not a state-machine breaking change.
   159    EmitProtoEventNonConsensus(ctx context.Context, event protoiface.MessageV1) error
   160  }
   161  ```
   162  
   163  Typed events emitted with `EmitProto`  should be assumed to be part of blockchain consensus (whether they are part of
   164  the block or app hash is left to the runtime to specify).
   165  
   166  Events emitted by `EmitKVEvent` and `EmitProtoEventNonConsensus` are not considered to be part of consensus and cannot be observed
   167  by other modules. If there is a client-side need to add events in patch releases, these methods can be used.
   168  
   169  #### Logger
   170  
   171  A logger (`cosmossdk.io/log`) must be supplied using `depinject`, and will
   172  be made available for modules to use via `depinject.In`.
   173  Modules using it should follow the current pattern in the SDK by adding the module name before using it.
   174  
   175  ```go
   176  type ModuleInputs struct {
   177    depinject.In
   178  
   179    Logger log.Logger
   180  }
   181  
   182  func ProvideModule(in ModuleInputs) ModuleOutputs {
   183    keeper := keeper.NewKeeper(
   184      in.logger,
   185    )
   186  }
   187  
   188  func NewKeeper(logger log.Logger) Keeper {
   189    return Keeper{
   190      logger: logger.With(log.ModuleKey, "x/"+types.ModuleName),
   191    }
   192  }
   193  ```
   194  
   195  ```
   196  
   197  ### Core `AppModule` extension interfaces
   198  
   199  
   200  Modules will provide their core services to the runtime module via extension interfaces built on top of the
   201  `cosmossdk.io/core/appmodule.AppModule` tag interface. This tag interface requires only two empty methods which
   202  allow `depinject` to identify implementors as `depinject.OnePerModule` types and as app module implementations:
   203  
   204  ```go
   205  type AppModule interface {
   206    depinject.OnePerModuleType
   207  
   208    // IsAppModule is a dummy method to tag a struct as implementing an AppModule.
   209    IsAppModule()
   210  }
   211  ```
   212  
   213  Other core extension interfaces will be defined in `cosmossdk.io/core` should be supported by valid runtime
   214  implementations.
   215  
   216  #### `MsgServer` and `QueryServer` registration
   217  
   218  `MsgServer` and `QueryServer` registration is done by implementing the `HasServices` extension interface:
   219  
   220  ```go
   221  type HasServices interface {
   222  	AppModule
   223  
   224  	RegisterServices(grpc.ServiceRegistrar)
   225  }
   226  
   227  ```
   228  
   229  Because of the `cosmos.msg.v1.service` protobuf option, required for `Msg` services, the same `ServiceRegitrar` can be
   230  used to register both `Msg` and query services.
   231  
   232  #### Genesis
   233  
   234  The genesis `Handler` functions - `DefaultGenesis`, `ValidateGenesis`, `InitGenesis` and `ExportGenesis` - are specified
   235  against the `GenesisSource` and `GenesisTarget` interfaces which will abstract over genesis sources which may be a single
   236  JSON object or collections of JSON objects that can be efficiently streamed.
   237  
   238  ```go
   239  // GenesisSource is a source for genesis data in JSON format. It may abstract over a
   240  // single JSON object or separate files for each field in a JSON object that can
   241  // be streamed over. Modules should open a separate io.ReadCloser for each field that
   242  // is required. When fields represent arrays they can efficiently be streamed
   243  // over. If there is no data for a field, this function should return nil, nil. It is
   244  // important that the caller closes the reader when done with it.
   245  type GenesisSource = func(field string) (io.ReadCloser, error)
   246  
   247  // GenesisTarget is a target for writing genesis data in JSON format. It may
   248  // abstract over a single JSON object or JSON in separate files that can be
   249  // streamed over. Modules should open a separate io.WriteCloser for each field
   250  // and should prefer writing fields as arrays when possible to support efficient
   251  // iteration. It is important the caller closers the writer AND checks the error
   252  // when done with it. It is expected that a stream of JSON data is written
   253  // to the writer.
   254  type GenesisTarget = func(field string) (io.WriteCloser, error)
   255  ```
   256  
   257  All genesis objects for a given module are expected to conform to the semantics of a JSON object.
   258  Each field in the JSON object should be read and written separately to support streaming genesis.
   259  The [ORM](./adr-055-orm.md) and [collections](./adr-062-collections-state-layer.md) both support
   260  streaming genesis and modules using these frameworks generally do not need to write any manual
   261  genesis code.
   262  
   263  To support genesis, modules should implement the `HasGenesis` extension interface:
   264  
   265  ```go
   266  type HasGenesis interface {
   267  	AppModule
   268  
   269  	// DefaultGenesis writes the default genesis for this module to the target.
   270  	DefaultGenesis(GenesisTarget) error
   271  
   272  	// ValidateGenesis validates the genesis data read from the source.
   273  	ValidateGenesis(GenesisSource) error
   274  
   275  	// InitGenesis initializes module state from the genesis source.
   276  	InitGenesis(context.Context, GenesisSource) error
   277  
   278  	// ExportGenesis exports module state to the genesis target.
   279  	ExportGenesis(context.Context, GenesisTarget) error
   280  }
   281  ```
   282  
   283  #### Pre Blockers
   284  
   285  Modules that have functionality that runs before BeginBlock and should implement the has `HasPreBlocker` interfaces:
   286  
   287  ```go
   288  type HasPreBlocker interface {
   289    AppModule
   290    PreBlock(context.Context) error
   291  }
   292  ```
   293  
   294  #### Begin and End Blockers
   295  
   296  Modules that have functionality that runs before transactions (begin blockers) or after transactions
   297  (end blockers) should implement the has `HasBeginBlocker` and/or `HasEndBlocker` interfaces:
   298  
   299  ```go
   300  type HasBeginBlocker interface {
   301    AppModule
   302    BeginBlock(context.Context) error
   303  }
   304  
   305  type HasEndBlocker interface {
   306    AppModule
   307    EndBlock(context.Context) error
   308  }
   309  ```
   310  
   311  The `BeginBlock` and `EndBlock` methods will take a `context.Context`, because:
   312  
   313  * most modules don't need Comet information other than `BlockInfo` so we can eliminate dependencies on specific
   314  Comet versions
   315  * for the few modules that need Comet block headers and/or return validator updates, specific versions of the
   316  runtime module will provide specific functionality for interacting with the specific version(s) of Comet
   317  supported
   318  
   319  In order for `BeginBlock`, `EndBlock` and `InitGenesis` to send back validator updates and retrieve full Comet
   320  block headers, the runtime module for a specific version of Comet could provide services like this:
   321  
   322  ```go
   323  type ValidatorUpdateService interface {
   324      SetValidatorUpdates(context.Context, []abci.ValidatorUpdate)
   325  }
   326  ```
   327  
   328  Header Service defines a way to get header information about a block. This information is generalized for all implementations: 
   329  
   330  ```go 
   331  
   332  type Service interface {
   333  	GetHeaderInfo(context.Context) Info
   334  }
   335  
   336  type Info struct {
   337  	Height int64      // Height returns the height of the block
   338  	Hash []byte       // Hash returns the hash of the block header
   339  	Time time.Time    // Time returns the time of the block
   340  	ChainID string    // ChainId returns the chain ID of the block
   341  }
   342  ```
   343  
   344  Comet Service provides a way to get comet specific information: 
   345  
   346  ```go
   347  type Service interface {
   348  	GetCometInfo(context.Context) Info
   349  }
   350  
   351  type CometInfo struct {
   352    Evidence []abci.Misbehavior // Misbehavior returns the misbehavior of the block
   353  	// ValidatorsHash returns the hash of the validators
   354  	// For Comet, it is the hash of the next validators
   355  	ValidatorsHash []byte
   356  	ProposerAddress []byte            // ProposerAddress returns the address of the block proposer
   357  	DecidedLastCommit abci.CommitInfo // DecidedLastCommit returns the last commit info
   358  }
   359  ```
   360  
   361  If a user would like to provide a module other information they would need to implement another service like:
   362  
   363  ```go
   364  type RollKit Interface {
   365    ...
   366  }
   367  ```
   368  
   369  We know these types will change at the Comet level and that also a very limited set of modules actually need this
   370  functionality, so they are intentionally kept out of core to keep core limited to the necessary, minimal set of stable
   371  APIs.
   372  
   373  #### Remaining Parts of AppModule
   374  
   375  The current `AppModule` framework handles a number of additional concerns which aren't addressed by this core API.
   376  These include:
   377  
   378  * gas
   379  * block headers
   380  * upgrades
   381  * registration of gogo proto and amino interface types
   382  * cobra query and tx commands
   383  * gRPC gateway 
   384  * crisis module invariants
   385  * simulations
   386  
   387  Additional `AppModule` extension interfaces either inside or outside of core will need to be specified to handle
   388  these concerns.
   389  
   390  In the case of gogo proto and amino interfaces, the registration of these generally should happen as early
   391  as possible during initialization and in [ADR 057: App Wiring](./adr-057-app-wiring-1.md), protobuf type registration  
   392  happens before dependency injection (although this could alternatively be done dedicated DI providers).
   393  
   394  gRPC gateway registration should probably be handled by the runtime module, but the core API shouldn't depend on gRPC
   395  gateway types as 1) we are already using an older version and 2) it's possible the framework can do this registration
   396  automatically in the future. So for now, the runtime module should probably provide some sort of specific type for doing
   397  this registration ex:
   398  
   399  ```go
   400  type GrpcGatewayInfo struct {
   401      Handlers []GrpcGatewayHandler
   402  }
   403  
   404  type GrpcGatewayHandler func(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error
   405  ```
   406  
   407  which modules can return in a provider:
   408  
   409  ```go
   410  func ProvideGrpcGateway() GrpcGatewayInfo {
   411      return GrpcGatewayinfo {
   412          Handlers: []Handler {types.RegisterQueryHandlerClient}
   413      }
   414  }
   415  ```
   416  
   417  Crisis module invariants and simulations are subject to potential redesign and should be managed with types
   418  defined in the crisis and simulation modules respectively.
   419  
   420  Extension interface for CLI commands will be provided via the `cosmossdk.io/client/v2` module and its
   421  [autocli](./adr-058-auto-generated-cli.md) framework.
   422  
   423  #### Example Usage
   424  
   425  Here is an example of setting up a hypothetical `foo` v2 module which uses the [ORM](./adr-055-orm.md) for its state
   426  management and genesis.
   427  
   428  ```go
   429  
   430  type Keeper struct {
   431  	db orm.ModuleDB
   432  	evtSrv event.Service
   433  }
   434  
   435  func (k Keeper) RegisterServices(r grpc.ServiceRegistrar) {
   436    foov1.RegisterMsgServer(r, k)
   437    foov1.RegisterQueryServer(r, k)
   438  }
   439  
   440  func (k Keeper) BeginBlock(context.Context) error {
   441  	return nil
   442  }
   443  
   444  func ProvideApp(config *foomodulev2.Module, evtSvc event.EventService, db orm.ModuleDB) (Keeper, appmodule.AppModule){
   445      k := &Keeper{db: db, evtSvc: evtSvc}
   446      return k, k
   447  }
   448  ```
   449  
   450  ### Runtime Compatibility Version
   451  
   452  The `core` module will define a static integer var, `cosmossdk.io/core.RuntimeCompatibilityVersion`, which is
   453  a minor version indicator of the core module that is accessible at runtime. Correct runtime module implementations
   454  should check this compatibility version and return an error if the current `RuntimeCompatibilityVersion` is higher
   455  than the version of the core API that this runtime version can support. When new features are adding to the `core`
   456  module API that runtime modules are required to support, this version should be incremented.
   457  
   458  ### Runtime Modules
   459  
   460  The initial `runtime` module will simply be created within the existing `github.com/cosmos/cosmos-sdk` go module
   461  under the `runtime` package. This module will be a small wrapper around the existing `BaseApp`, `sdk.Context` and
   462  module manager and follow the Cosmos SDK's existing [0-based versioning](https://0ver.org). To move to semantic
   463  versioning as well as runtime modularity, new officially supported runtime modules will be created under the
   464  `cosmossdk.io/runtime` prefix. For each supported consensus engine a semantically-versioned go module should be created
   465  with a runtime implementation for that consensus engine. For example:
   466  - `cosmossdk.io/runtime/comet`
   467  - `cosmossdk.io/runtime/comet/v2`
   468  - `cosmossdk.io/runtime/rollkit`
   469  - etc.
   470  
   471  These runtime modules should attempt to be semantically versioned even if the underlying consensus engine is not. Also,
   472  because a runtime module is also a first class Cosmos SDK module, it should have a protobuf module config type.
   473  A new semantically versioned module config type should be created for each of these runtime module such that there is a
   474  1:1 correspondence between the go module and module config type. This is the same practice should be followed for every 
   475  semantically versioned Cosmos SDK module as described in [ADR 057: App Wiring](./adr-057-app-wiring.md).
   476  
   477  Currently, `github.com/cosmos/cosmos-sdk/runtime` uses the protobuf config type `cosmos.app.runtime.v1alpha1.Module`.
   478  When we have a standalone v1 comet runtime, we should use a dedicated protobuf module config type such as
   479  `cosmos.runtime.comet.v1.Module1`. When we release v2 of the comet runtime (`cosmossdk.io/runtime/comet/v2`) we should
   480  have a corresponding `cosmos.runtime.comet.v2.Module` protobuf type.
   481  
   482  In order to make it easier to support different consensus engines that support the same core module functionality as
   483  described in this ADR, a common go module should be created with shared runtime components. The easiest runtime components
   484  to share initially are probably the message/query router, inter-module client, service register, and event router.
   485  This common runtime module should be created initially as the `cosmossdk.io/runtime/common` go module.
   486  
   487  When this new architecture has been implemented, the main dependency for a Cosmos SDK module would be
   488  `cosmossdk.io/core` and that module should be able to be used with any supported consensus engine (to the extent
   489  that it does not explicitly depend on consensus engine specific functionality such as Comet's block headers). An
   490  app developer would then be able to choose which consensus engine they want to use by importing the corresponding
   491  runtime module. The current `BaseApp` would be refactored into the `cosmossdk.io/runtime/comet` module, the router
   492  infrastructure in `baseapp/` would be refactored into `cosmossdk.io/runtime/common` and support ADR 033, and eventually
   493  a dependency on `github.com/cosmos/cosmos-sdk` would no longer be required.
   494  
   495  In short, modules would depend primarily on `cosmossdk.io/core`, and each `cosmossdk.io/runtime/{consensus-engine}`
   496  would implement the `cosmossdk.io/core` functionality for that consensus engine.
   497  
   498  On additional piece that would need to be resolved as part of this architecture is how runtimes relate to the server.
   499  Likely it would make sense to modularize the current server architecture so that it can be used with any runtime even
   500  if that is based on a consensus engine besides Comet. This means that eventually the Comet runtime would need to
   501  encapsulate the logic for starting Comet and the ABCI app.
   502  
   503  ### Testing
   504  
   505  A mock implementation of all services should be provided in core to allow for unit testing of modules
   506  without needing to depend on any particular version of runtime. Mock services should
   507  allow tests to observe service behavior or provide a non-production implementation - for instance memory
   508  stores can be used to mock stores.
   509  
   510  For integration testing, a mock runtime implementation should be provided that allows composing different app modules
   511  together for testing without a dependency on runtime or Comet.
   512  
   513  ## Consequences
   514  
   515  ### Backwards Compatibility
   516  
   517  Early versions of runtime modules should aim to support as much as possible modules built with the existing
   518  `AppModule`/`sdk.Context` framework. As the core API is more widely adopted, later runtime versions may choose to
   519  drop support and only support the core API plus any runtime module specific APIs (like specific versions of Comet).
   520  
   521  The core module itself should strive to remain at the go semantic version `v1` as long as possible and follow design
   522  principles that allow for strong long-term support (LTS).
   523  
   524  Older versions of the SDK can support modules built against core with adaptors that convert wrap core `AppModule`
   525  implementations in implementations of `AppModule` that conform to that version of the SDK's semantics as well
   526  as by providing service implementations by wrapping `sdk.Context`.
   527  
   528  ### Positive
   529  
   530  * better API encapsulation and separation of concerns
   531  * more stable APIs
   532  * more framework extensibility
   533  * deterministic events and queries
   534  * event listeners
   535  * inter-module msg and query execution support
   536  * more explicit support for forking and merging of module versions (including runtime)
   537  
   538  ### Negative
   539  
   540  ### Neutral
   541  
   542  * modules will need to be refactored to use this API
   543  * some replacements for `AppModule` functionality still need to be defined in follow-ups
   544    (type registration, commands, invariants, simulations) and this will take additional design work
   545  
   546  ## Further Discussions
   547  
   548  * gas
   549  * block headers
   550  * upgrades
   551  * registration of gogo proto and amino interface types
   552  * cobra query and tx commands
   553  * gRPC gateway
   554  * crisis module invariants
   555  * simulations
   556  
   557  ## References
   558  
   559  * [ADR 033: Protobuf-based Inter-Module Communication](./adr-033-protobuf-inter-module-comm.md)
   560  * [ADR 057: App Wiring](./adr-057-app-wiring-1.md)
   561  * [ADR 055: ORM](./adr-055-orm.md)
   562  * [ADR 028: Public Key Addresses](./adr-028-public-key-addresses.md)
   563  * [Keeping Your Modules Compatible](https://go.dev/blog/module-compatibility)