Just to add an example:
let's add the pallet-assets to the substrate-node-template.
- First, we add the dependency and the feature in the runtime/Cargo.toml file
The dependency is required for obtaining the pallet-assets source code. As for the std/no_std feature, have a look at this post.
- Second, we specify the configuration for pallet-assets in our runtime/src/lib.rs
In other words, we are implementing the functionality for assets in our chain but we have to configure the pallet's types. These types, known as associated types (for pallet-assets: Balances, AssetId, etc), implement certain traits e.g.:
type Currency: ReservableCurrencySelf::AccountId;
The Currency type is declared which will be used in this pallet and it implements (or has to implement) the ReservableCurrencySelf::AccountId trait. So we need to specify what Currency is in our runtime and it has to implement the ReservableCurrencySelf::AccountId trait. If we look how pallet-assets is configured in statemint:
type Currency = Balances;
where Balances is coming from pallet-balances. Pallet-balances needs to implement the ReservableCurrencySelf::AccountId trait which is the case as you can see here.
These first two steps are required for any pallet you want to add to your runtime. However, for some pallets there are some additional steps required e.g. for pallet_assets the genesis configuration. This allows to define the genesis configuration for the pallet (ref) which needs to be done in node/src/chain_spec.rs like here.
P.S. What is helping me a lot with understanding substrate code is literally following the code (I use rust-analyzer) and see where this function/variable/trait/type is coming from.