diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock
index fb1c4bea80e87fd19b9d162a846b72be8c0397e2..3223f4e9e843a3f001d8a339ff6030bd1a0a20f0 100644
--- a/polkadot/Cargo.lock
+++ b/polkadot/Cargo.lock
@@ -6179,6 +6179,7 @@ dependencies = [
  "polkadot-node-subsystem",
  "polkadot-node-subsystem-util",
  "polkadot-primitives",
+ "polkadot-procmacro-overseer-subsystems-gen",
  "sc-client-api",
  "sp-api",
  "sp-core",
@@ -6229,6 +6230,17 @@ dependencies = [
  "thiserror",
 ]
 
+[[package]]
+name = "polkadot-procmacro-overseer-subsystems-gen"
+version = "0.1.0"
+dependencies = [
+ "assert_matches",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "trybuild",
+]
+
 [[package]]
 name = "polkadot-procmacro-subsystem-dispatch-gen"
 version = "0.1.0"
diff --git a/polkadot/node/overseer/Cargo.toml b/polkadot/node/overseer/Cargo.toml
index 674c22eb8069ff6d5ace30c0a57c475f512aa865..3ecb90f45ffae2e824672263a1815e83727f8509 100644
--- a/polkadot/node/overseer/Cargo.toml
+++ b/polkadot/node/overseer/Cargo.toml
@@ -12,6 +12,7 @@ futures = "0.3.12"
 futures-timer = "3.0.2"
 polkadot-node-primitives = { package = "polkadot-node-primitives", path = "../primitives" }
 polkadot-node-subsystem-util = { path = "../subsystem-util" }
+polkadot-procmacro-overseer-subsystems-gen = { path = "./subsystems-gen" }
 polkadot-primitives = { path = "../../primitives" }
 polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../subsystem" }
 tracing = "0.1.25"
diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs
index f16576f6531c85b3cdbb3096655566d045258b23..9f7b6239a21879e54e2583d3c0eee7b10d131010 100644
--- a/polkadot/node/overseer/src/lib.rs
+++ b/polkadot/node/overseer/src/lib.rs
@@ -94,6 +94,7 @@ pub use polkadot_subsystem::{
 };
 use polkadot_node_subsystem_util::{TimeoutExt, metrics::{self, prometheus}, metered, Metronome};
 use polkadot_node_primitives::SpawnNamed;
+use polkadot_procmacro_overseer_subsystems_gen::AllSubsystemsGen;
 
 // A capacity of bounded channels inside the overseer.
 const CHANNEL_CAPACITY: usize = 1024;
@@ -143,7 +144,7 @@ impl<Client> HeadSupportsParachains for Arc<Client> where
 /// Each [`Subsystem`] is supposed to implement some interface that is generic over
 /// message type that is specific to this [`Subsystem`]. At the moment not all
 /// subsystems are implemented and the rest can be mocked with the [`DummySubsystem`].
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, AllSubsystemsGen)]
 pub struct AllSubsystems<
 	CV = (), CB = (), CS = (), SD = (), AD = (), AR = (), BS = (), BD = (), P = (),
 	RA = (), AS = (), NB = (), CA = (), CG = (), CP = (), ApD = (), ApV = (),
@@ -244,492 +245,6 @@ impl<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS>
 		}
 	}
 
-	/// Replace the `candidate_validation` instance in `self`.
-	pub fn replace_candidate_validation<NEW>(
-		self,
-		candidate_validation: NEW,
-	) -> AllSubsystems<NEW, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `candidate_backing` instance in `self`.
-	pub fn replace_candidate_backing<NEW>(
-		self,
-		candidate_backing: NEW,
-	) -> AllSubsystems<CV, NEW, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `candidate_selection` instance in `self`.
-	pub fn replace_candidate_selection<NEW>(
-		self,
-		candidate_selection: NEW,
-	) -> AllSubsystems<CV, CB, NEW, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `statement_distribution` instance in `self`.
-	pub fn replace_statement_distribution<NEW>(
-		self,
-		statement_distribution: NEW,
-	) -> AllSubsystems<CV, CB, CS, NEW, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `availability_distribution` instance in `self`.
-	pub fn replace_availability_distribution<NEW>(
-		self,
-		availability_distribution: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, NEW, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `availability_recovery` instance in `self`.
-	pub fn replace_availability_recovery<NEW>(
-		self,
-		availability_recovery: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, NEW, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `bitfield_signing` instance in `self`.
-	pub fn replace_bitfield_signing<NEW>(
-		self,
-		bitfield_signing: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, AR, NEW, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `bitfield_distribution` instance in `self`.
-	pub fn replace_bitfield_distribution<NEW>(
-		self,
-		bitfield_distribution: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, NEW, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `provisioner` instance in `self`.
-	pub fn replace_provisioner<NEW>(
-		self,
-		provisioner: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, NEW, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `runtime_api` instance in `self`.
-	pub fn replace_runtime_api<NEW>(
-		self,
-		runtime_api: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, NEW, AS, NB, CA, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `availability_store` instance in `self`.
-	pub fn replace_availability_store<NEW>(
-		self,
-		availability_store: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, NEW, NB, CA, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `network_bridge` instance in `self`.
-	pub fn replace_network_bridge<NEW>(
-		self,
-		network_bridge: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NEW, CA, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `chain_api` instance in `self`.
-	pub fn replace_chain_api<NEW>(
-		self,
-		chain_api: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, NEW, CG, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `collation_generation` instance in `self`.
-	pub fn replace_collation_generation<NEW>(
-		self,
-		collation_generation: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, NEW, CP, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `collator_protocol` instance in `self`.
-	pub fn replace_collator_protocol<NEW>(
-		self,
-		collator_protocol: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, NEW, ApD, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `approval_distribution` instance in `self`.
-	pub fn replace_approval_distribution<NEW>(
-		self,
-		approval_distribution: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, NEW, ApV, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `approval_voting` instance in `self`.
-	pub fn replace_approval_voting<NEW>(
-		self,
-		approval_voting: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, NEW, GS> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting,
-			gossip_support: self.gossip_support,
-		}
-	}
-
-	/// Replace the `gossip_support` instance in `self`.
-	pub fn replace_gossip_support<NEW>(
-		self,
-		gossip_support: NEW,
-	) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, NEW> {
-		AllSubsystems {
-			candidate_validation: self.candidate_validation,
-			candidate_backing: self.candidate_backing,
-			candidate_selection: self.candidate_selection,
-			statement_distribution: self.statement_distribution,
-			availability_distribution: self.availability_distribution,
-			availability_recovery: self.availability_recovery,
-			bitfield_signing: self.bitfield_signing,
-			bitfield_distribution: self.bitfield_distribution,
-			provisioner: self.provisioner,
-			runtime_api: self.runtime_api,
-			availability_store: self.availability_store,
-			network_bridge: self.network_bridge,
-			chain_api: self.chain_api,
-			collation_generation: self.collation_generation,
-			collator_protocol: self.collator_protocol,
-			approval_distribution: self.approval_distribution,
-			approval_voting: self.approval_voting,
-			gossip_support,
-		}
-	}
-
 	fn as_ref(&self) -> AllSubsystems<&'_ CV, &'_ CB, &'_ CS, &'_ SD, &'_ AD, &'_ AR, &'_ BS, &'_ BD, &'_ P, &'_ RA, &'_ AS, &'_ NB, &'_ CA, &'_ CG, &'_ CP, &'_ ApD, &'_ ApV, &'_ GS> {
 		AllSubsystems {
 			candidate_validation: &self.candidate_validation,
diff --git a/polkadot/node/overseer/subsystems-gen/Cargo.toml b/polkadot/node/overseer/subsystems-gen/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..2720663c293d0464bf2f8f3340f4ed46600accdd
--- /dev/null
+++ b/polkadot/node/overseer/subsystems-gen/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "polkadot-procmacro-overseer-subsystems-gen"
+version = "0.1.0"
+authors = ["Parity Technologies <admin@parity.io>"]
+edition = "2018"
+description = "Small proc macro to create mocking level iface type helpers"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = { version = "1.0.60", features = ["full", "extra-traits"] }
+quote = "1.0.9"
+proc-macro2 = "1.0.24"
+assert_matches = "1.5.0"
+
+[dev-dependencies]
+trybuild = "1.0.41"
diff --git a/polkadot/node/overseer/subsystems-gen/src/lib.rs b/polkadot/node/overseer/subsystems-gen/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c15d08bb04f4addd173502034051145c2ac21a3c
--- /dev/null
+++ b/polkadot/node/overseer/subsystems-gen/src/lib.rs
@@ -0,0 +1,179 @@
+// Copyright 2021 Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+use std::collections::HashSet;
+
+use proc_macro2::TokenStream;
+use quote::quote;
+
+use syn::{Error, GenericParam, Ident, Result, Type, parse2};
+
+#[proc_macro_derive(AllSubsystemsGen)]
+pub fn subsystems_gen(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
+	let item: TokenStream = item.into();
+	impl_subsystems_gen(item).unwrap_or_else(|err| err.to_compile_error()).into()
+}
+
+fn impl_subsystems_gen(item: TokenStream) -> Result<proc_macro2::TokenStream> {
+	let span = proc_macro2::Span::call_site();
+	let ds = parse2::<syn::ItemStruct>(item.clone())?;
+
+	match ds.fields {
+		syn::Fields::Named(named) => {
+			#[derive(Clone)]
+			struct NameTyTup {
+				field: Ident,
+				ty: Type,
+			}
+			let mut orig_generics = ds.generics;
+			// remove default types
+			orig_generics.params = orig_generics.params.into_iter().map(|mut generic| {
+				match generic {
+					GenericParam::Type(ref mut param) => {
+						param.eq_token = None;
+						param.default = None;
+					}
+					_ => {}
+				}
+				generic
+			}).collect();
+
+			// prepare a hashmap of generic type to member that uses it
+			let generic_types = orig_generics.params.iter().filter_map(|generic| {
+				if let GenericParam::Type(param) = generic {
+					Some(param.ident.clone())
+				} else {
+					None
+				}
+			}).collect::<HashSet<Ident>>();
+
+			let strukt_ty = ds.ident;
+
+			if generic_types.is_empty() {
+				return Err(Error::new(strukt_ty.span(), "struct must have at least one generic parameter."))
+			}
+
+			// collect all fields that exist, and all fields that are replaceable
+			let mut replacable_items = Vec::<NameTyTup>::with_capacity(64);
+			let mut all_fields = replacable_items.clone();
+
+
+			let mut duplicate_generic_detection = HashSet::<Ident>::with_capacity(64);
+
+			for field in named.named {
+				let field_ident = field.ident.clone().ok_or_else(|| Error::new(span, "Member field must have a name."))?;
+				let ty = field.ty.clone();
+				let ntt = NameTyTup { field: field_ident, ty };
+
+				replacable_items.push(ntt.clone());
+
+
+				// assure every generic is used exactly once
+				let ty_ident = match field.ty {
+					Type::Path(path) => path.path.get_ident().cloned().ok_or_else(|| {
+						Error::new(proc_macro2::Span::call_site(),  "Expected an identifier, but got a path.")
+					}),
+					_ => return Err(Error::new(proc_macro2::Span::call_site(), "Must be path."))
+				}?;
+
+				if generic_types.contains(&ty_ident) {
+					if let Some(previous) = duplicate_generic_detection.replace(ty_ident) {
+						return Err(Error::new(previous.span(), "Generic type parameters may only be used for exactly one field, but is used more than once."))
+					}
+				}
+
+				all_fields.push(ntt);
+			}
+
+
+			let msg = "Generated by #[derive(AllSubsystemsGen)] derive proc-macro.";
+			let mut additive = TokenStream::new();
+
+			// generate an impl of `fn replace_#name`
+			for NameTyTup { field: replacable_item, ty: replacable_item_ty } in replacable_items {
+				let keeper = all_fields.iter().filter(|ntt| ntt.field != replacable_item).map(|ntt| ntt.field.clone());
+				let strukt_ty = strukt_ty.clone();
+				let fname = Ident::new(&format!("replace_{}", replacable_item), span);
+				// adjust the generics such that the appropriate member type is replaced
+				let mut modified_generics = orig_generics.clone();
+				modified_generics.params = modified_generics.params.into_iter().map(|mut generic| {
+					match generic {
+						GenericParam::Type(ref mut param) => {
+							param.eq_token = None;
+							param.default = None;
+							if match &replacable_item_ty {
+								Type::Path(path) =>
+									path.path.get_ident().filter(|&ident| ident == &param.ident).is_some(),
+								_ => false
+							} {
+								param.ident = Ident::new("NEW", span);
+							}
+						}
+						_ => {}
+					}
+					generic
+				}).collect();
+
+				additive.extend(quote! {
+					impl #orig_generics #strukt_ty #orig_generics {
+						#[doc = #msg]
+						pub fn #fname < NEW > (self, replacement: NEW) -> #strukt_ty #modified_generics {
+							#strukt_ty :: #modified_generics {
+								#replacable_item: replacement,
+								#(
+									#keeper: self.#keeper,
+								)*
+							}
+						}
+					}
+				});
+			}
+
+			Ok(additive)
+		}
+		syn::Fields::Unit => Err(Error::new(span, "Must be a struct with named fields. Not an unit struct.")),
+		syn::Fields::Unnamed(_) => {
+			Err(Error::new(span, "Must be a struct with named fields. Not an unnamed fields struct."))
+		}
+	}
+}
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+
+	#[test]
+	fn basic() {
+		let item = quote! {
+			pub struct AllSubsystems<A,B,CD> {
+				pub a: A,
+				pub beee: B,
+				pub dj: CD,
+			}
+		};
+
+		let output = impl_subsystems_gen(item).expect("Simple example always works. qed");
+		println!("//generated:");
+		println!("{}", output);
+	}
+
+	#[test]
+	fn ui() {
+		let t = trybuild::TestCases::new();
+		t.compile_fail("tests/ui/err-*.rs");
+		t.pass("tests/ui/ok-*.rs");
+	}
+}
diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.rs b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.rs
new file mode 100644
index 0000000000000000000000000000000000000000..318636279ea5ba5323affed9792083a3a7790ad6
--- /dev/null
+++ b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.rs
@@ -0,0 +1,13 @@
+#![allow(dead_code)]
+
+use polkadot_procmacro_overseer_subsystems_gen::AllSubsystemsGen;
+
+#[derive(Clone, AllSubsystemsGen)]
+enum AllSubsystems<A,B> {
+	A(A),
+	B(B),
+}
+
+fn main() {
+	let all = AllSubsystems::<u8,u16>::A(0u8);
+}
diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.stderr b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..5f61df1057cb0c34c5f8a2f7dcd2247503912110
--- /dev/null
+++ b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.stderr
@@ -0,0 +1,5 @@
+error: expected `struct`
+ --> $DIR/err-01-enum.rs:6:1
+  |
+6 | enum AllSubsystems<A,B> {
+  | ^^^^
diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.rs b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f89939d5c306edffddca742d530ddc6207f006af
--- /dev/null
+++ b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.rs
@@ -0,0 +1,17 @@
+#![allow(dead_code)]
+
+use polkadot_procmacro_overseer_subsystems_gen::AllSubsystemsGen;
+
+#[derive(Clone, AllSubsystemsGen)]
+struct AllSubsystems<X> {
+	a: X,
+	b: X,
+}
+
+fn main() {
+	let all = AllSubsystems::<u16> {
+		a: 0_u16,
+		b: 1_u16,
+	};
+	let _all = all.replace_a(77u8);
+}
diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.stderr b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..23e1404ff822c7ee9c5419300a27fe2130e2e804
--- /dev/null
+++ b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.stderr
@@ -0,0 +1,14 @@
+error: Generic type parameters may only be used for exactly one field, but is used more than once.
+ --> $DIR/err-01-generic-used-twice.rs:7:5
+  |
+7 |     a: X,
+  |        ^
+
+error[E0599]: no method named `replace_a` found for struct `AllSubsystems<u16>` in the current scope
+  --> $DIR/err-01-generic-used-twice.rs:16:17
+   |
+6  | struct AllSubsystems<X> {
+   | ----------------------- method `replace_a` not found for this
+...
+16 |     let _all = all.replace_a(77u8);
+   |                    ^^^^^^^^^ method not found in `AllSubsystems<u16>`
diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generic.rs b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generic.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0466eb444cd962d97f0dcc326526d06366a04e7a
--- /dev/null
+++ b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generic.rs
@@ -0,0 +1,17 @@
+#![allow(dead_code)]
+
+use polkadot_procmacro_overseer_subsystems_gen::AllSubsystemsGen;
+
+#[derive(Clone, AllSubsystemsGen)]
+struct AllSubsystems {
+	a: f32,
+	b: u16,
+}
+
+fn main() {
+	let all = AllSubsystems {
+		a: 0_f32,
+		b: 1_u16,
+	};
+	let _all = all.replace_a(77u8);
+}
diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generics.stderr b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generics.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..1de880ae433cbc83c8b6fba5a401cd6e3d4a4517
--- /dev/null
+++ b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generics.stderr
@@ -0,0 +1,14 @@
+error: Generic type parameters may only be used once have at least one generic parameter.
+ --> $DIR/err-01-no-generics.rs:7:5
+  |
+7 |     a: X,
+  |        ^
+
+error[E0599]: no method named `replace_a` found for struct `AllSubsystems<u16>` in the current scope
+  --> $DIR/err-01-no-generics.rs:16:17
+   |
+6  | struct AllSubsystems<X> {
+   | ----------------------- method `replace_a` not found for this
+...
+16 |     let _all = all.replace_a(77u8);
+   |                    ^^^^^^^^^ method not found in `AllSubsystems<u16>`
diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/ok-01-w-generics.rs b/polkadot/node/overseer/subsystems-gen/tests/ui/ok-01-w-generics.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1519990a0a5580bb5920508193e1491bf2cec7ff
--- /dev/null
+++ b/polkadot/node/overseer/subsystems-gen/tests/ui/ok-01-w-generics.rs
@@ -0,0 +1,17 @@
+#![allow(dead_code)]
+
+use polkadot_procmacro_overseer_subsystems_gen::AllSubsystemsGen;
+
+#[derive(Clone, AllSubsystemsGen)]
+struct AllSubsystems<A, B> {
+    a: A,
+    b: B,
+}
+
+fn main() {
+    let all = AllSubsystems::<u8, u16> {
+        a: 0u8,
+        b: 1u16,
+    };
+    let _all: AllSubsystems<_,_> = all.replace_a::<u32>(777_777u32);
+}