diff --git a/docs/architecture.svg b/docs/architecture.svg
new file mode 100644
index 00000000000..354aaf3dda8
--- /dev/null
+++ b/docs/architecture.svg
@@ -0,0 +1,39 @@
+
\ No newline at end of file
diff --git a/docs/coding-guidelines.md b/docs/coding-guidelines.md
new file mode 100644
index 00000000000..a208dd2768c
--- /dev/null
+++ b/docs/coding-guidelines.md
@@ -0,0 +1,287 @@
+# Coding Guidelines
+
+
+**Table of Contents**
+
+- [Coding Guidelines](#coding-guidelines)
+ - [Hierarchical State Machines](#hierarchical-state-machines)
+ - [Conventions for `poll` implementations](#conventions-for-poll-implementations)
+ - [Prioritize local work over new work from a remote](#prioritize-local-work-over-new-work-from-a-remote)
+ - [Bound everything](#bound-everything)
+ - [Channels](#channels)
+ - [Local queues](#local-queues)
+ - [Further reading](#further-reading)
+ - [No premature optimizations](#no-premature-optimizations)
+ - [Keep things sequential unless proven to be slow](#keep-things-sequential-unless-proven-to-be-slow)
+ - [Use `async/await` for sequential execution only](#use-asyncawait-for-sequential-execution-only)
+ - [Don't communicate by sharing memory; share memory by communicating.](#dont-communicate-by-sharing-memory-share-memory-by-communicating)
+ - [Further Reading](#further-reading)
+ - [Use iteration not recursion](#use-iteration-not-recursion)
+ - [Further Reading](#further-reading-1)
+
+
+
+
+Below is a set of coding guidelines followed across the rust-libp2p code base.
+
+## Hierarchical State Machines
+
+If you sqint, rust-libp2p is just a big hierarchy of [state
+machines](https://en.wikipedia.org/wiki/Finite-state_machine) where parents pass
+events down to their children and children pass events up to their parents.
+
+![Architecture](architecture.svg)
+
+
+ Reproduce diagram
+
+ ```
+ @startuml
+ Swarm <|-- RootBehaviour
+ Swarm <|-- ConnectionPool
+ Swarm <|-- Transport
+ RootBehaviour <|-- PingBehaviour
+ RootBehaviour <|-- IdentifyBehaviour
+ RootBehaviour <|-- KademliaBehaviour
+
+ Swarm : poll()
+ RootBehaviour : poll()
+ ConnectionPool : poll()
+ Transport : poll()
+ PingBehaviour : poll()
+ IdentifyBehaviour : poll()
+ KademliaBehaviour : poll()
+ @enduml
+ ```
+
+
+Using hierarchical state machines is a deliberate choice throughout the
+rust-libp2p code base. It makes reasoning about control and data flow simple. It
+works well with Rust's `Future` model. It allows fine-grain control e.g. on the
+order child state machines are polled.
+
+The above comes with downsides. It feels more verbose. The mix of control flow (`loop`, `return`,
+`break`, `continue`) in `poll` functions together with the asynchronous and thus decoupled
+communication via events can be very hard to understand. Both are a form of complexity that we are
+trading for correctness and performance which aligns with Rust's and rust-libp2p's goals.
+
+The architecture pattern of hierarchical state machines should be used wherever possible.
+
+### Conventions for `poll` implementations
+
+The `poll` method of a single state machine can be complex especially when that
+state machine itself `poll`s many child state machines. The patterns shown below
+have proven useful and should be followed across the code base.
+
+``` rust
+fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll{
+ loop {
+ match self.child_1.poll(cx) {
+ // The child made progress.
+ Poll::Ready(_) => {
+ // Either return an event to the parent:
+ return Poll::Ready(todo!());
+ // or `continue`, thus polling `child_1` again. `child_1` can potentially make more progress. Try to exhaust
+ // it before moving on to the next child.
+ continue
+ // but NEVER move to the next child if the current child made progress. Given
+ // that the current child might be able to make more progress, it did not yet
+ // register the waker in order for the root task to be woken up later on. Moving
+ // on to the next child might result in the larger `Future` task to stall as it
+ // assumes that there is no more progress to be made.
+ }
+
+ // The child did not make progress. It has registered the waker for a
+ // later wake up. Proceed with the other children.
+ Poll::Pending(_) => {}
+ }
+
+ match self.child_2.poll(cx) {
+ Poll::Ready(child_2_event) => {
+ // Events can be dispatched from one child to the other.
+ self.child_1.handle_event(child_2_event);
+
+ // Either `continue` thus polling `child_1` again, or `return Poll::Ready` with a result to the parent.
+ todo!()
+ }
+ Poll::Pending(_) => {}
+ }
+
+ match self.child_3.poll(cx) {
+ Poll::Ready(__) => {
+ // Either `continue` thus polling `child_1` again, or `return Poll::Ready` with a result to the parent.
+ todo!()
+ }
+ Poll::Pending(_) => {}
+ }
+
+ // None of the child state machines can make any more progress. Each registered
+ // the waker in order for the root `Future` task to be woken up again.
+ return Poll::Pending
+ }
+}
+```
+
+### Prioritize local work over new work from a remote
+
+When handling multiple work streams, prioritize local work items over
+accepting new work items from a remote. Take the following state machine as an
+example, reading and writing from a socket, returning result to its parent:
+
+``` rust
+struct SomeStateMachine {
+ socket: Socket,
+ events_to_return_to_parent: VecDeque,
+ messages_to_send_on_socket: VecDeque,
+}
+
+impl Stream for SomeStateMachine {
+ type Item = Event;
+
+ fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll