diff --git a/substrate/core/client/src/lib.rs b/substrate/core/client/src/lib.rs
index 441b2480e9..595bf55b32 100644
--- a/substrate/core/client/src/lib.rs
+++ b/substrate/core/client/src/lib.rs
@@ -15,6 +15,62 @@
// along with Substrate. If not, see .
//! Substrate Client and associated logic.
+//!
+//! The [`Client`] is one of the most important components of Substrate. It mainly comprises two
+//! parts:
+//!
+//! - A database containing the blocks and chain state, generally referred to as
+//! the [`Backend`](backend::Backend).
+//! - A runtime environment, generally referred to as the [`Executor`](CallExecutor).
+//!
+//! # Initialization
+//!
+//! Creating a [`Client`] is done by calling the `new` method and passing to it a
+//! [`Backend`](backend::Backend) and an [`Executor`](CallExecutor).
+//!
+//! The former is typically provided by the `substrate-client-db` crate, but [`in_mem::Backend`]
+//! can be used for testing purposes.
+//!
+//! The latter typically requires passing one of:
+//!
+//! - A [`LocalCallExecutor`] running the runtime locally.
+//! - A [`RemoteCallExecutor`](light::call_executor::RemoteCallExecutor) that will ask a
+//! third-party to perform the executions.
+//! - A [`RemoteOrLocalCallExecutor`](light::call_executor::RemoteOrLocalCallExecutor), combination
+//! of the two.
+//!
+//! Additionally, the fourth generic parameter of the `Client` is a marker type representing
+//! the ways in which the runtime can interface with the outside. Any code that builds a `Client`
+//! is responsible for putting the right marker.
+//!
+//! ## Example
+//!
+//! ```
+//! use std::sync::Arc;
+//! use substrate_client::{Client, in_mem::Backend, LocalCallExecutor};
+//! use primitives::Blake2Hasher;
+//! use runtime_primitives::{StorageOverlay, ChildrenStorageOverlay};
+//! use executor::NativeExecutor;
+//!
+//! // In this example, we're using the `Block` and `RuntimeApi` types from the
+//! // `substrate-test-runtime-client` crate. These types are automatically generated when
+//! // compiling a runtime. In a typical use-case, these types would have been to be generated
+//! // from your runtime.
+//! use test_client::{LocalExecutor, runtime::Block, runtime::RuntimeApi};
+//!
+//! let backend = Arc::new(Backend::::new());
+//! let client = Client::<_, _, _, RuntimeApi>::new(
+//! backend.clone(),
+//! LocalCallExecutor::new(
+//! backend.clone(),
+//! NativeExecutor::::new(None)
+//! ),
+//! // This parameter provides the storage for the chain genesis.
+//! <(StorageOverlay, ChildrenStorageOverlay)>::default(),
+//! Default::default()
+//! );
+//! ```
+//!
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]