diff --git a/docs/sdk/src/guides/async_backing_guide.rs b/docs/sdk/src/guides/async_backing_guide.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f2f4dcabfd29b872de2981cd0a9c5003118c29c0
--- /dev/null
+++ b/docs/sdk/src/guides/async_backing_guide.rs
@@ -0,0 +1,254 @@
+//! # Upgrade Parachain for Asynchronous Backing Compatibility
+//!
+//! This guide is relevant for cumulus based parachain projects started in 2023 or before, whose
+//! backing process is synchronous where parablocks can only be built on the latest Relay Chain
+//! block. Async Backing allows collators to build parablocks on older Relay Chain blocks and create
+//! pipelines of multiple pending parablocks. This parallel block generation increases efficiency
+//! and throughput. For more information on Async backing and its terminology, refer to the document
+//! on [the Polkadot Wiki.](https://wiki.polkadot.network/docs/maintain-guides-async-backing)
+//!
+//! > If starting a new parachain project, please use an async backing compatible template such as
+//! > the
+//! > [parachain template](https://github.com/paritytech/polkadot-sdk/tree/master/templates/parachain).
+//! The rollout process for Async Backing has three phases. Phases 1 and 2 below put new
+//! infrastructure in place. Then we can simply turn on async backing in phase 3.
+//!
+//! ## Prerequisite
+//!
+//! The relay chain needs to have async backing enabled so double-check that the relay-chain
+//! configuration contains the following three parameters (especially when testing locally e.g. with
+//! zombienet):
+//!
+//! ```json
+//! "async_backing_params": {
+//!     "max_candidate_depth": 3,
+//!     "allowed_ancestry_len": 2
+//! },
+//! "scheduling_lookahead": 2
+//! ```
+//!
+//! <div class="warning">`scheduling_lookahead` must be set to 2, otherwise parachain block times
+//! will degrade to worse than with sync backing!</div>
+//!
+//! ## Phase 1 - Update Parachain Runtime
+//!
+//! This phase involves configuring your parachain’s runtime `/runtime/src/lib.rs` to make use of
+//! async backing system.
+//!
+//! 1. Establish and ensure constants for `capacity` and `velocity` are both set to 1 in the
+//!    runtime.
+//! 2. Establish and ensure the constant relay chain slot duration measured in milliseconds equal to
+//!    `6000` in the runtime.
+//! ```rust
+//! // Maximum number of blocks simultaneously accepted by the Runtime, not yet included into the
+//! // relay chain.
+//! pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 1;
+//! // How many parachain blocks are processed by the relay chain per parent. Limits the number of
+//! // blocks authored per slot.
+//! pub const BLOCK_PROCESSING_VELOCITY: u32 = 1;
+//! // Relay chain slot duration, in milliseconds.
+//! pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
+//! ```
+//!
+//! 3. Establish constants `MILLISECS_PER_BLOCK` and `SLOT_DURATION` if not already present in the
+//!    runtime.
+//! ```ignore
+//! // `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked
+//! // up by `pallet_aura` to implement `fn slot_duration()`.
+//! //
+//! // Change this to adjust the block time.
+//! pub const MILLISECS_PER_BLOCK: u64 = 12000;
+//! pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
+//! ```
+//!
+//! 4. Configure `cumulus_pallet_parachain_system` in the runtime.
+//!
+//! - Define a `FixedVelocityConsensusHook` using our capacity, velocity, and relay slot duration
+//! constants. Use this to set the parachain system `ConsensusHook` property.
+#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", ConsensusHook)]
+//! ```ignore
+//! impl cumulus_pallet_parachain_system::Config for Runtime {
+//!     ..
+//!     type ConsensusHook = ConsensusHook;
+//!     ..
+//! }
+//! ```
+//! - Set the parachain system property `CheckAssociatedRelayNumber` to
+//! `RelayNumberMonotonicallyIncreases`
+//! ```ignore
+//! impl cumulus_pallet_parachain_system::Config for Runtime {
+//! 	..
+//! 	type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases;
+//! 	..
+//! }
+//! ```
+//!
+//! 5. Configure `pallet_aura` in the runtime.
+//!
+//! - Set `AllowMultipleBlocksPerSlot` to `false` (don't worry, we will set it to `true` when we
+//! activate async backing in phase 3).
+//!
+//! - Define `pallet_aura::SlotDuration` using our constant `SLOT_DURATION`
+//! ```ignore
+//! impl pallet_aura::Config for Runtime {
+//! 	..
+//! 	type AllowMultipleBlocksPerSlot = ConstBool<false>;
+//! 	#[cfg(feature = "experimental")]
+//! 	type SlotDuration = ConstU64<SLOT_DURATION>;
+//! 	..
+//! }
+//! ```
+//!
+//! 6. Update `sp_consensus_aura::AuraApi::slot_duration` in `sp_api::impl_runtime_apis` to match
+//!    the constant `SLOT_DURATION`
+#![doc = docify::embed!("../../templates/parachain/runtime/src/apis.rs", impl_slot_duration)]
+//!
+//! 7. Implement the `AuraUnincludedSegmentApi`, which allows the collator client to query its
+//!    runtime to determine whether it should author a block.
+//!
+//!    - Add the dependency `cumulus-primitives-aura` to the `runtime/Cargo.toml` file for your
+//!      runtime
+//! ```ignore
+//! ..
+//! cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false }
+//! ..
+//! ```
+//!
+//! - In the same file, add `"cumulus-primitives-aura/std",` to the `std` feature.
+//!
+//! - Inside the `impl_runtime_apis!` block for your runtime, implement the
+//!   `cumulus_primitives_aura::AuraUnincludedSegmentApi` as shown below.
+#![doc = docify::embed!("../../templates/parachain/runtime/src/apis.rs", impl_can_build_upon)]
+//!
+//! **Note:** With a capacity of 1 we have an effective velocity of ½ even when velocity is
+//! configured to some larger value. This is because capacity will be filled after a single block is
+//! produced and will only be freed up after that block is included on the relay chain, which takes
+//! 2 relay blocks to accomplish. Thus with capacity 1 and velocity 1 we get the customary 12 second
+//! parachain block time.
+//!
+//! 8. If your `runtime/src/lib.rs` provides a `CheckInherents` type to `register_validate_block`,
+//!    remove it. `FixedVelocityConsensusHook` makes it unnecessary. The following example shows how
+//!    `register_validate_block` should look after removing `CheckInherents`.
+#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", register_validate_block)]
+//!
+//!
+//! ## Phase 2 - Update Parachain Nodes
+//!
+//! This phase consists of plugging in the new lookahead collator node.
+//!
+//! 1. Import `cumulus_primitives_core::ValidationCode` to `node/src/service.rs`.
+#![doc = docify::embed!("../../templates/parachain/node/src/service.rs", cumulus_primitives)]
+//!
+//! 2. In `node/src/service.rs`, modify `sc_service::spawn_tasks` to use a clone of `Backend` rather
+//!    than the original
+//! ```ignore
+//! sc_service::spawn_tasks(sc_service::SpawnTasksParams {
+//!     ..
+//!     backend: backend.clone(),
+//!     ..
+//! })?;
+//! ```
+//!
+//! 3. Add `backend` as a parameter to `start_consensus()` in `node/src/service.rs`
+//! ```text
+//! fn start_consensus(
+//!     ..
+//!     backend: Arc<ParachainBackend>,
+//!     ..
+//! ```
+//! ```ignore
+//! if validator {
+//!   start_consensus(
+//!     ..
+//!     backend.clone(),
+//!     ..
+//!    )?;
+//! }
+//! ```
+//!
+//! 4. In `node/src/service.rs` import the lookahead collator rather than the basic collator
+#![doc = docify::embed!("../../templates/parachain/node/src/service.rs", lookahead_collator)]
+//!
+//! 5. In `start_consensus()` replace the `BasicAuraParams` struct with `AuraParams`
+//!    - Change the struct type from `BasicAuraParams` to `AuraParams`
+//!    - In the `para_client` field, pass in a cloned para client rather than the original
+//!    - Add a `para_backend` parameter after `para_client`, passing in our para backend
+//!    - Provide a `code_hash_provider` closure like that shown below
+//!    - Increase `authoring_duration` from 500 milliseconds to 1500
+//! ```ignore
+//! let params = AuraParams {
+//!     ..
+//!     para_client: client.clone(),
+//!     para_backend: backend.clone(),
+//!     ..
+//!     code_hash_provider: move |block_hash| {
+//!         client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash())
+//!     },
+//!     ..
+//!     authoring_duration: Duration::from_millis(1500),
+//!     ..
+//! };
+//! ```
+//!
+//! **Note:** Set `authoring_duration` to whatever you want, taking your own hardware into account.
+//! But if the backer who should be slower than you due to reading from disk, times out at two
+//! seconds your candidates will be rejected.
+//!
+//! 6. In `start_consensus()` replace `basic_aura::run` with `aura::run`
+//! ```ignore
+//! let fut =
+//! aura::run::<Block, sp_consensus_aura::sr25519::AuthorityPair, _, _, _, _, _, _, _, _, _>(
+//!    params,
+//! );
+//! task_manager.spawn_essential_handle().spawn("aura", None, fut);
+//! ```
+//!
+//! ## Phase 3 - Activate Async Backing
+//!
+//! This phase consists of changes to your parachain’s runtime that activate async backing feature.
+//!
+//! 1. Configure `pallet_aura`, setting `AllowMultipleBlocksPerSlot` to true in
+//!    `runtime/src/lib.rs`.
+#![doc = docify::embed!("../../templates/parachain/runtime/src/configs/mod.rs", aura_config)]
+//!
+//! 2. Increase the maximum `UNINCLUDED_SEGMENT_CAPACITY` in `runtime/src/lib.rs`.
+#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", async_backing_params)]
+//!
+//! 3. Decrease `MILLISECS_PER_BLOCK` to 6000.
+//!
+//! - Note: For a parachain which measures time in terms of its own block number rather than by
+//!   relay block number it may be preferable to increase velocity. Changing block time may cause
+//!   complications, requiring additional changes. See the section “Timing by Block Number”.
+#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", block_times)]
+//!
+//! 4. Update `MAXIMUM_BLOCK_WEIGHT` to reflect the increased time available for block production.
+#![doc = docify::embed!("../../templates/parachain/runtime/src/lib.rs", max_block_weight)]
+//!
+//! 5. Add a feature flagged alternative for `MinimumPeriod` in `pallet_timestamp`. The type should
+//!    be `ConstU64<0>` with the feature flag experimental, and `ConstU64<{SLOT_DURATION / 2}>`
+//!    without.
+//! ```ignore
+//! impl pallet_timestamp::Config for Runtime {
+//!     ..
+//!     #[cfg(feature = "experimental")]
+//!     type MinimumPeriod = ConstU64<0>;
+//!     #[cfg(not(feature = "experimental"))]
+//!     type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>;
+//!     ..
+//! }
+//! ```
+//!
+//! ## Timing by Block Number
+//!
+//! With asynchronous backing it will be possible for parachains to opt for a block time of 6
+//! seconds rather than 12 seconds. But modifying block duration isn’t so simple for a parachain
+//! which was measuring time in terms of its own block number. It could result in expected and
+//! actual time not matching up, stalling the parachain.
+//!
+//! One strategy to deal with this issue is to instead rely on relay chain block numbers for timing.
+//! Relay block number is kept track of by each parachain in `pallet-parachain-system` with the
+//! storage value `LastRelayChainBlockNumber`. This value can be obtained and used wherever timing
+//! based on block number is needed.
+
+#![deny(rustdoc::broken_intra_doc_links)]
+#![deny(rustdoc::private_intra_doc_links)]
diff --git a/docs/sdk/src/guides/mod.rs b/docs/sdk/src/guides/mod.rs
index 485cdc30636f2751145188c5d13a99b7d6fd6726..8296ed447e143d3fadcfbb2157786eea8934947f 100644
--- a/docs/sdk/src/guides/mod.rs
+++ b/docs/sdk/src/guides/mod.rs
@@ -36,5 +36,8 @@ pub mod your_first_runtime;
 /// How to enable storage weight reclaiming in a parachain node and runtime.
 pub mod enable_pov_reclaim;
 
+/// How to enable Async Backing on parachain projects that started in 2023 or before.
+pub mod async_backing_guide;
+
 /// How to enable metadata hash verification in the runtime.
 pub mod enable_metadata_hash;
diff --git a/templates/parachain/node/src/service.rs b/templates/parachain/node/src/service.rs
index 587dd19faf3e8af6e8a8296333d61393e6fd52ac..bf44207acc9c6bc29d12862308d656d940592d5c 100644
--- a/templates/parachain/node/src/service.rs
+++ b/templates/parachain/node/src/service.rs
@@ -12,6 +12,7 @@ use parachain_template_runtime::{
 
 // Cumulus Imports
 use cumulus_client_collator::service::CollatorService;
+#[docify::export(lookahead_collator)]
 use cumulus_client_consensus_aura::collators::lookahead::{self as aura, Params as AuraParams};
 use cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport;
 use cumulus_client_consensus_proposer::Proposer;
@@ -20,6 +21,7 @@ use cumulus_client_service::{
 	BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, ParachainHostFunctions,
 	StartRelayChainTasksParams,
 };
+#[docify::export(cumulus_primitives)]
 use cumulus_primitives_core::{
 	relay_chain::{CollatorPair, ValidationCode},
 	ParaId,
diff --git a/templates/parachain/runtime/src/apis.rs b/templates/parachain/runtime/src/apis.rs
index 107956ded410464757c767b81d1ac88da9aba1a4..bc3c3808fb78bcde9114e7aac4e6794136be1e12 100644
--- a/templates/parachain/runtime/src/apis.rs
+++ b/templates/parachain/runtime/src/apis.rs
@@ -47,10 +47,26 @@ use super::{
 	SLOT_DURATION, VERSION,
 };
 
+// we move some impls outside so we can easily use them with `docify`.
+impl Runtime {
+	#[docify::export]
+	fn impl_slot_duration() -> sp_consensus_aura::SlotDuration {
+		sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION)
+	}
+
+	#[docify::export]
+	fn impl_can_build_upon(
+		included_hash: <Block as BlockT>::Hash,
+		slot: cumulus_primitives_aura::Slot,
+	) -> bool {
+		ConsensusHook::can_build_upon(included_hash, slot)
+	}
+}
+
 impl_runtime_apis! {
 	impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
 		fn slot_duration() -> sp_consensus_aura::SlotDuration {
-			sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION)
+			Runtime::impl_slot_duration()
 		}
 
 		fn authorities() -> Vec<AuraId> {
@@ -63,7 +79,7 @@ impl_runtime_apis! {
 			included_hash: <Block as BlockT>::Hash,
 			slot: cumulus_primitives_aura::Slot,
 		) -> bool {
-			ConsensusHook::can_build_upon(included_hash, slot)
+			Runtime::impl_can_build_upon(included_hash, slot)
 		}
 	}
 
diff --git a/templates/parachain/runtime/src/configs/mod.rs b/templates/parachain/runtime/src/configs/mod.rs
index 8a410a27e4a7bd270f661ec0b7cd99c8ef1416f9..204e74a11e6a195bc2ef05302ff2695034659ecd 100644
--- a/templates/parachain/runtime/src/configs/mod.rs
+++ b/templates/parachain/runtime/src/configs/mod.rs
@@ -265,6 +265,7 @@ impl pallet_session::Config for Runtime {
 	type WeightInfo = ();
 }
 
+#[docify::export(aura_config)]
 impl pallet_aura::Config for Runtime {
 	type AuthorityId = AuraId;
 	type DisabledValidators = ();
diff --git a/templates/parachain/runtime/src/lib.rs b/templates/parachain/runtime/src/lib.rs
index 6e6491a19adf6b41f4fcfa28aa62f6bf97e12b3e..3a9fee38f245da179f2bf6c093740b64b4754ffb 100644
--- a/templates/parachain/runtime/src/lib.rs
+++ b/templates/parachain/runtime/src/lib.rs
@@ -168,17 +168,21 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
 	state_version: 1,
 };
 
-/// This determines the average expected block time that we are targeting.
-/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`.
-/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked
-/// up by `pallet_aura` to implement `fn slot_duration()`.
-///
-/// Change this to adjust the block time.
-pub const MILLISECS_PER_BLOCK: u64 = 6000;
-
-// NOTE: Currently it is not possible to change the slot duration after the chain has started.
-//       Attempting to do so will brick block production.
-pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
+#[docify::export]
+mod block_times {
+	/// This determines the average expected block time that we are targeting. Blocks will be
+	/// produced at a minimum duration defined by `SLOT_DURATION`. `SLOT_DURATION` is picked up by
+	/// `pallet_timestamp` which is in turn picked up by `pallet_aura` to implement `fn
+	/// slot_duration()`.
+	///
+	/// Change this to adjust the block time.
+	pub const MILLISECS_PER_BLOCK: u64 = 6000;
+
+	// NOTE: Currently it is not possible to change the slot duration after the chain has started.
+	// Attempting to do so will brick block production.
+	pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
+}
+pub use block_times::*;
 
 // Time is measured by number of blocks.
 pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
@@ -201,21 +205,27 @@ const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5);
 /// `Operational` extrinsics.
 const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
 
+#[docify::export(max_block_weight)]
 /// We allow for 2 seconds of compute with a 6 second average block time.
 const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(
 	WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2),
 	cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64,
 );
 
-/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included
-/// into the relay chain.
-const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3;
-/// How many parachain blocks are processed by the relay chain per parent. Limits the
-/// number of blocks authored per slot.
-const BLOCK_PROCESSING_VELOCITY: u32 = 1;
-/// Relay chain slot duration, in milliseconds.
-const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
+#[docify::export]
+mod async_backing_params {
+	/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included
+	/// into the relay chain.
+	pub(crate) const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3;
+	/// How many parachain blocks are processed by the relay chain per parent. Limits the
+	/// number of blocks authored per slot.
+	pub(crate) const BLOCK_PROCESSING_VELOCITY: u32 = 1;
+	/// Relay chain slot duration, in milliseconds.
+	pub(crate) const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
+}
+pub(crate) use async_backing_params::*;
 
+#[docify::export]
 /// Aura consensus hook
 type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook<
 	Runtime,
@@ -293,6 +303,7 @@ mod runtime {
 	pub type TemplatePallet = pallet_parachain_template;
 }
 
+#[docify::export(register_validate_block)]
 cumulus_pallet_parachain_system::register_validate_block! {
 	Runtime = Runtime,
 	BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,