diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 4e3aec7f4e52a9c84da7bd1c741d52758cade4ac..0000000000000000000000000000000000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,24 +0,0 @@
----
-name: Bug report
-about: Report problems and unexpected behaviour
-title: ''
-labels: bug
-assignees: ''
-
----
-
-**Describe the bug**
-A clear and concise description of what the bug is.
-
-**To Reproduce**
-Steps to reproduce the behaviour, ideally with source code:
-
-1. ...
-2. ...
-3. ...
-
-**Expected behaviour**
-A clear and concise description of what you expected to happen.
-
-**Additional context**
-Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 5d424b505f22e83501f293cd2602ddac5abc33fe..0000000000000000000000000000000000000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: Feature request
-about: Suggest an idea
-title: ''
-labels: feature
-assignees: ''
-
----
-
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
-
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
-
-**Additional context**
-Add any other context about the feature request here.
diff --git a/README.md b/README.md
index 921e8485195a83ca3dbc882c867d44ea94a1cd79..db962907c2d7003d757478126e85fa7c189aecd0 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
 
 websocket is a minimal and idiomatic WebSocket library for Go.
 
-At minimum Go 1.12 is required as websocket uses a new [feature](https://github.com/golang/go/issues/26937#issuecomment-415855861) in net/http
+Go 1.12 is required as it uses a new [feature](https://github.com/golang/go/issues/26937#issuecomment-415855861) in net/http
 to perform WebSocket handshakes.
 
 This library is not final and the API is subject to change.
@@ -19,13 +19,11 @@ go get nhooyr.io/websocket
 
 ## Features
 
-- Full support of the WebSocket protocol
-- Zero dependencies outside of the stdlib
-- Very minimal and carefully considered API
-- context.Context is first class
-- net/http is used for WebSocket dials and upgrades
+- Minimal yet pragmatic API
+- First class context.Context support
 - Thoroughly tested, fully passes the [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite)
-- All returned errors include detailed context
+- Concurrent writes
+- Zero dependencies outside of the stdlib
 
 ## Roadmap
 
@@ -97,45 +95,45 @@ c.Close(websocket.StatusNormalClosure, "")
 
 ## Design considerations
 
-- Minimal API is easier to maintain and for others to learn
+- Minimal API is easier to maintain and learn
 - Context based cancellation is more ergonomic and robust than setting deadlines
-- No pings or pongs because TCP keep alives work fine for HTTP/1.1 and they do not make
+- No ping support because TCP keep alives work fine for HTTP/1.1 and they do not make
   sense with HTTP/2 (see #1)
 - net.Conn is never exposed as WebSocket's over HTTP/2 will not have a net.Conn.
-- Functional options make the API very clean and easy to extend
+- Structures are nicer than functional options, see [google/go-cloud#908](https://github.com/google/go-cloud/issues/908#issuecomment-445034143)
 - Using net/http's Client for dialing means we do not have to reinvent dialing hooks
-  and configurations. Just pass in a custom net/http client if you want custom dialing.
+  and configurations like other WebSocket libraries
 
 ## Comparison
 
 While I believe nhooyr/websocket has a better API than existing libraries, 
 both gorilla/websocket and gobwas/ws were extremely useful in implementing the
-WebSocket protocol correctly so big thanks to the authors of both. In particular,
+WebSocket protocol correctly so **big thanks** to the authors of both. In particular,
 I made sure to go through the issue tracker of gorilla/websocket to make sure
-I implemented details correctly.
+I implemented details correctly and understood how people were using the package
+in production.
 
 ### gorilla/websocket
 
 https://github.com/gorilla/websocket
 
-This package is the community standard but it is very old and over time
-has accumulated cruft. There are many ways to do the same thing and the API
-is not clear. Just compare the godoc of
+This package is the community standard but it is 6 years old and over time
+has accumulated cruft. There are many ways to do the same thing, usage is not clear
+and there are some rough edges. Just compare the godoc of
 [nhooyr/websocket](godoc.org/github.com/nhooyr/websocket) side by side with
 [gorilla/websocket](godoc.org/github.com/gorilla/websocket).
 
 The API for nhooyr/websocket has been designed such that there is only one way to do things
-which makes using it correctly and safely much easier.
-
-In terms of lines of code, this library is around 2000 whereas gorilla/websocket is
-at 7000. So while the API for nhooyr/websocket is simpler, the implementation is also
-significantly simpler and easier to test which reduces the surface are of bugs.
+which makes it easy to use correctly.
 
 Furthermore, nhooyr/websocket has support for newer Go idioms such as context.Context and
 also uses net/http's Client and ResponseWriter directly for WebSocket handshakes.
 gorilla/websocket writes its handshakes directly to a net.Conn which means
 it has to reinvent hooks for TLS and proxying and prevents support of HTTP/2.
 
+Another advantage of nhooyr/websocket is that it supports multiple concurrent writers out
+of the box.
+
 ### x/net/websocket
 
 https://godoc.org/golang.org/x/net/websocket
@@ -149,12 +147,12 @@ See https://github.com/golang/go/issues/18152
 https://github.com/gobwas/ws
 
 This library has an extremely flexible API but that comes at the cost of usability
-and clarity. Its not clear what the best way to do anything is.
+and clarity. 
 
 This library is fantastic in terms of performance. The author put in significant
 effort to ensure its speed and I have applied as many of its optimizations as
-I could into nhooyr/websocket.
+I could into nhooyr/websocket. Definitely check out his fantastic [blog post](https://medium.freecodecamp.org/million-websockets-and-go-cc58418460bb) about performant WebSocket servers.
 
 If you want a library that gives you absolute control over everything, this is the library,
-but for most users, the API provided by nhooyr/websocket will definitely fit better as it will
-be just as performant but much easier to use correctly.
+but for most users, the API provided by nhooyr/websocket will fit better as it is just as
+performant but much easier to use correctly and idiomatic.
diff --git a/doc.go b/doc.go
index 22e6327378945db92ef16c2057a0fabdcebb90ab..246170a2a4401831c53134a7925f1a61f85eb90e 100644
--- a/doc.go
+++ b/doc.go
@@ -2,8 +2,12 @@
 //
 // See https://tools.ietf.org/html/rfc6455
 //
-// The echo example is the best way to understand how to correctly use the library.
+// Please see https://nhooyr.io/websocket for thorough overview docs and a
+// comparison with existing implementations.
+//
+// Conn, Dial, and Accept are the main entrypoints into this package. Use Dial to dial
+// a WebSocket server, Accept to accept a WebSocket client dial and then Conn to interact
+// with the resulting WebSocket connections.
 //
-// Please see https://nhooyr.io/websocket for detailed design docs and a comparison with existing
-// libraries.
+// The echo example is the best way to understand how to correctly use the library.
 package websocket
diff --git a/docs/CONTRIBUTING.md b/docs/contributing.md
similarity index 84%
rename from docs/CONTRIBUTING.md
rename to docs/contributing.md
index b33e722d2767ee517d5be755c2b66991f1c05a5f..3f267ef2985f2ed4fe333a460057db556dcde8aa 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/contributing.md
@@ -1,4 +1,10 @@
-# Contributing Guidelines
+# Contributing
+
+## Issues
+
+Please be as descriptive as possible with your description.
+
+## Pull requests
 
 Please split up changes into several small descriptive commits.
 
diff --git a/example_echo_test.go b/example_echo_test.go
index a196affb7d94de346813fa19fc023943a4d79717..d2867a45431ab43777e58e10c9189495da918f5d 100644
--- a/example_echo_test.go
+++ b/example_echo_test.go
@@ -17,7 +17,13 @@ import (
 	"nhooyr.io/websocket"
 )
 
+// Example_echo starts a WebSocket echo server and
+// then dials the server and sends 5 different messages
+// and prints out the server's responses.
 func Example_echo() {
+	// First we listen on port 0, that means the OS will
+	// assign us a random free port. This is the listener
+	// the server will serve on and the client will connect to.
 	l, err := net.Listen("tcp", "localhost:0")
 	if err != nil {
 		log.Fatalf("failed to listen: %v", err)
@@ -37,6 +43,7 @@ func Example_echo() {
 	}
 	defer s.Close()
 
+	// This starts the echo server on the listener.
 	go func() {
 		err := s.Serve(l)
 		if err != http.ErrServerClosed {
@@ -44,6 +51,7 @@ func Example_echo() {
 		}
 	}()
 
+	// Now we dial the server and send the messages.
 	err = client("ws://" + l.Addr().String())
 	if err != nil {
 		log.Fatalf("client failed: %v", err)
@@ -57,6 +65,9 @@ func Example_echo() {
 	// {"i":4}
 }
 
+// echoServer is the WebSocket echo server implementation.
+// It ensures the client speaks the echo subprotocol and
+// only allows one message every 100ms with a 10 message burst.
 func echoServer(w http.ResponseWriter, r *http.Request) error {
 	c, err := websocket.Accept(w, r, websocket.AcceptOptions{
 		Subprotocols: []string{"echo"},
@@ -82,6 +93,10 @@ func echoServer(w http.ResponseWriter, r *http.Request) error {
 	}
 }
 
+// echo reads from the websocket connection and then writes
+// the received message back to it.
+// It only waits 1 minute to read and write the message and
+// limits the received message to 32768 bytes.
 func echo(ctx context.Context, c *websocket.Conn, l *rate.Limiter) error {
 	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
@@ -111,6 +126,9 @@ func echo(ctx context.Context, c *websocket.Conn, l *rate.Limiter) error {
 	return err
 }
 
+// client dials the WebSocket echo server at the given url.
+// It then sends it 5 different messages and echo's the server's
+// response to each.
 func client(url string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
 	defer cancel()
diff --git a/websocket.go b/websocket.go
index 0c3c3eef9f68e15ea1dbf14d6d3444505495c43d..21c4ef3f5130a9a6fa4d74f5c15080d94e8345c8 100644
--- a/websocket.go
+++ b/websocket.go
@@ -429,6 +429,8 @@ func (c *Conn) writeControl(ctx context.Context, opcode opcode, p []byte) error
 // a WebSocket message of type dataType to the connection.
 // Ensure you close the writer once you have written the entire message.
 // Concurrent calls to Writer are ok.
+// Writer will block if there is another goroutine with an open writer
+// until writer is closed.
 func (c *Conn) Writer(ctx context.Context, typ MessageType) (io.WriteCloser, error) {
 	select {
 	case <-c.closed: