diff --git a/.travis.yml b/.travis.yml
index 2c946354..2ef163b0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,15 +26,15 @@ env:
 before_script:
   - |
       if [ "$TRAVIS_OS_NAME" = 'windows' ]; then
-        powershell -executionpolicy bypass -file _build/cargo-make.ps1 -version "0.15.3" -target "x86_64-pc-windows-msvc"
+        powershell -executionpolicy bypass -file _build/cargo-make.ps1 -version "0.19.1" -target "x86_64-pc-windows-msvc"
       fi
   - |
       if [ "$TRAVIS_OS_NAME" = 'linux' ]; then
-        _build/cargo-make.sh "0.15.3" "x86_64-unknown-linux-musl"
+        _build/cargo-make.sh "0.19.1" "x86_64-unknown-linux-musl"
       fi
   - |
       if [ "$TRAVIS_OS_NAME" = 'osx' ]; then
-        _build/cargo-make.sh "0.15.3" "x86_64-apple-darwin"
+        _build/cargo-make.sh "0.19.1" "x86_64-apple-darwin"
       fi
 script:
   - cargo make workspace-ci-flow --no-workspace
diff --git a/Makefile.toml b/Makefile.toml
index bfcca9a4..a5c5c7c5 100644
--- a/Makefile.toml
+++ b/Makefile.toml
@@ -2,16 +2,87 @@
 [env]
 CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = "true"
 
-# Run `cargo make release` to push a new minor release of every crate.
+#
+# Run `RELEASE_LEVEL=(patch|major|minor) cargo make release` to push a new release of every crate.
+# Run `RELEASE_LEVEL=(patch|major|minor) CARGO_MAKE_WORKSPACE_SKIP_MEMBERS="crate1;crate2;" cargo make release`
+# to push a new release of some crates.
+#
+
 [tasks.release]
-args = ["release", "--config", "../_build/release.toml"]
+condition = { env_set = [ "RELEASE_LEVEL" ] }
+workspace = false
+env = { "CARGO_MAKE_WORKSPACE_SKIP_MEMBERS" = "integration_tests/*" }
+run_task = { name = "release-INTERNAL", fork = true }
 
-# Run `cargo make release-patch` to push a new patch release of every crate.
-[tasks.release-patch]
-args = ["release", "--config", "../_build/release.toml", "patch"]
 
-# Run `cargo make release-dry-run` to do a dry run.
+[tasks.release-some]
+condition = { env_set = [ "RELEASE_LEVEL", "CARGO_MAKE_WORKSPACE_SKIP_MEMBERS" ] }
+workspace = false
+env = { "CARGO_MAKE_WORKSPACE_SKIP_MEMBERS" = "integration_tests/*" }
+run_task = { name = "release-some-INTERNAL", fork = true }
+
+# Hack to filter out crates we do not want to release.
+# See <https://github.com/sagiegurari/cargo-make/issues/212#issuecomment-481123564>
+[tasks.release-INTERNAL]
+private = true
+condition = { env_set = [ "RELEASE_LEVEL" ] }
+command = "cargo-release"
+args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/../_build/release.toml", "${RELEASE_LEVEL}"]
+
+
+#
+# Run `RELEASE_LEVEL=(patch|major|minor) cargo make release-dry-run` to do a dry run.
+# Run `RELEASE_LEVEL=(patch|major|minor) CARGO_MAKE_WORKSPACE_SKIP_MEMBERS="crate1;crate2;" cargo make release-some-dry-run`
+# to do a dry run with some crates.
+#
+
 [tasks.release-dry-run]
+condition = { env_set = [ "RELEASE_LEVEL" ] }
+workspace = false
+env = { "CARGO_MAKE_WORKSPACE_SKIP_MEMBERS" = "integration_tests/*" }
+run_task = { name = "release-dry-run-INTERNAL", fork = true }
+
+[tasks.release-some-dry-run]
+condition = { env_set = [ "CARGO_MAKE_WORKSPACE_SKIP_MEMBERS", "RELEASE_LEVEL" ] }
+workspace = false
+run_task = { name = "release-some-dry-run-INTERNAL", fork = true }
+
+
+# Hack to filter out crates we do not want to release.
+# See <https://github.com/sagiegurari/cargo-make/issues/212#issuecomment-481123564>
+[tasks.release-dry-run-INTERNAL]
+private = true
+condition = { env_set = [ "RELEASE_LEVEL" ] }
+env = { "CARGO_MAKE_WORKSPACE_SKIP_MEMBERS" = "integration_tests/*" }
 description = "Run `cargo-release --dry-run` for every crate"
-command = "${HOME}/src/cargo-release/target/debug/cargo-release"
-args = ["release", "--config", "../_build/release.toml", "--dry-run"]
+command = "cargo-release"
+args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/../_build/release.toml", "--dry-run", "${RELEASE_LEVEL}"]
+
+#
+# Run `RELEASE_LEVEL=(patch|major|minor) cargo make release-local-test` to do actually make changes locally but
+# not push them up to crates.io or Github.
+# Run `RELEASE_LEVEL=(patch|major|minor) cargo make release-some-local-test` to do actually make changes locally but
+# not push some crates up to crates.io or Github.
+#
+
+[tasks.release-local-test]
+condition = { env_set = [ "RELEASE_LEVEL" ] }
+workspace = false
+env = { "CARGO_MAKE_WORKSPACE_SKIP_MEMBERS" = "integration_tests/*" }
+run_task = { name = "release-local-test-INTERNAL", fork = true }
+
+
+[tasks.release-some-local-test]
+condition = { env_set = [ "CARGO_MAKE_WORKSPACE_SKIP_MEMBERS", "RELEASE_LEVEL" ] }
+workspace = false
+run_task = { name = "release-some-local-test-INTERNAL", fork = true }
+
+
+# Hack to filter out crates we do not want to release.
+# See <https://github.com/sagiegurari/cargo-make/issues/212#issuecomment-481123564>
+[tasks.release-local-test-INTERNAL]
+private = true
+condition = { env_set = [ "RELEASE_LEVEL" ] }
+description = "Run `cargo-release` for every crate, but only make changes locally"
+command = "cargo-release"
+args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/../_build/release.toml", "--no-confirm", "--skip-publish", "--skip-push", "--skip-tag", "${RELEASE_LEVEL}"]
diff --git a/RELEASING.md b/RELEASING.md
new file mode 100644
index 00000000..1946f092
--- /dev/null
+++ b/RELEASING.md
@@ -0,0 +1,72 @@
+# How to release new crate versions
+
+## Prerequisites
+
+We use [`cargo-make`](cargo-make) and [`cargo-release`](cargo-release) to automate crate releases. You will need to install them locally:
+
+- `cargo install -f cargo-make`
+- `cargo install -f cargo-release`
+
+## Preparing for a release
+
+There are two general classes of release and each require running different automation commands:
+
+1. All public workspace crates should be released and all share the same release level ("patch", "minor", "major"). _These commands take the form `release-[whatever]`._
+
+2. A subset of workspace crates need to be released, or not all crate releases share the same release level. _These commands start with `release-skip-[whatever]`._
+
+## Determine new release level
+
+For each crate, determine the desired release level (`patch`, `minor`, `major`). Set the `RELEASE_LEVEL` env variable to the desired release level.
+
+## Determine which crates to exclude
+
+If a subset of workspace crates need to be released, or not all crate releases share the same release level, set the `CARGO_MAKE_WORKSPACE_SKIP_MEMBERS` env
+variable to filter out specific workspace crates. The value is a list of semicolon-delineated crate names or a regular expression.
+
+**Important:** You likely want to always exclude `integration_tests/*`.
+
+## Dry run
+
+It is a good idea to do a dry run to sanity check what actions will be performed.
+
+- For case #1 above, run `cargo make release-dry-run`.
+
+  If the command finishes super quickly with no output you likely did not set `RELEASE_LEVEL`.
+
+- For case #2 above, run `cargo make release-some-dry-run`.
+
+  If the command finishes super quickly with no output you likely did not set `RELEASE_LEVEL` or `CARGO_MAKE_WORKSPACE_SKIP_MEMBERS`.
+
+## Local test
+
+Not everything is captured in the dry run. It is a good idea to run a local test.
+In a local test, all the release actions are performed on your local checkout
+but nothing is pushed to Github or crates.io.
+
+- For case #1 above, run `cargo make release-local-test`.
+
+  If the command finishes super quickly with no output you likely did not set `RELEASE_LEVEL`.
+
+- For case #2 above, run `cargo make release-some-local-test`.
+
+  If the command finishes super quickly with no output you likely did not set `RELEASE_LEVEL` or `CARGO_MAKE_WORKSPACE_SKIP_MEMBERS`.
+
+After, your local git repository should have the changes ready to push to Github.
+Use `git rebase -i HEAD~10` and drop the new commits.
+
+## Release
+
+After testing locally and via a dry run, it is time to release. A release
+consists of bumping versions, starting a new changelog section, pushing a tag to Github, and updating crates.io. This should all be handled by running the automated commands.
+
+- For case #1 above, run `cargo make release`.
+
+  If the command finishes super quickly with no output you likely did not set `RELEASE_LEVEL`.
+
+- For case #2 above, run `cargo make release-some`.
+
+  If the command finishes super quickly with no output you likely did not set `RELEASE_LEVEL` or `CARGO_MAKE_WORKSPACE_SKIP_MEMBERS`.
+
+[cargo-make]: https://github.com/sagiegurari/cargo-make
+[cargo-release]: https://github.com/sunng87/cargo-release
diff --git a/_build/azure-pipelines-template.yml b/_build/azure-pipelines-template.yml
index 2fc7a22b..8b023b1c 100644
--- a/_build/azure-pipelines-template.yml
+++ b/_build/azure-pipelines-template.yml
@@ -36,15 +36,15 @@ jobs:
     displayName: Query rust and cargo versions
   - ${{ if eq(parameters.name, 'Linux') }}:
     # Linux.
-    - script: _build/cargo-make.sh "0.15.3" "x86_64-unknown-linux-musl"
+    - script: _build/cargo-make.sh "0.19.1" "x86_64-unknown-linux-musl"
       displayName: Install cargo-make binary
   - ${{ if eq(parameters.name, 'macOS') }}:
     # Mac.
-    - script: _build/cargo-make.sh "0.15.3" "x86_64-apple-darwin"
+    - script: _build/cargo-make.sh "0.19.1" "x86_64-apple-darwin"
       displayName: Install cargo-make binary
   - ${{ if eq(parameters.name, 'Windows') }}:
     # Windows.
-    - script: powershell -executionpolicy bypass -file _build/cargo-make.ps1 -version "0.15.3" -target "x86_64-pc-windows-msvc"
+    - script: powershell -executionpolicy bypass -file _build/cargo-make.ps1 -version "0.19.1" -target "x86_64-pc-windows-msvc"
       displayName: Install cargo-make binary
   - script: cargo make workspace-ci-flow --no-workspace
     env: { CARGO_MAKE_RUN_CODECOV: true }
diff --git a/_build/release.toml b/_build/release.toml
index 8fb5cd4b..6aadd80b 100644
--- a/_build/release.toml
+++ b/_build/release.toml
@@ -4,5 +4,5 @@ pro-release-commit-message = "Bump {{crate_name}} version to {{next_version}}"
 tag-message = "Release {{crate_name}} {{version}}"
 upload-doc = false
 pre-release-replacements = [
-  {file="CHANGELOG.md", search="# master", replace="# master\n\n- No changes yet\n\n# [[{{version}}] {{date}}](https://github.com/graphql-rust/juniper/releases/tag/{{crate_name}}-{{version}})"},
+  {file="CHANGELOG.md", search="# master", replace="# master\n\n- Compatibility with the latest `juniper`.\n\n# [[{{version}}] {{date}}](https://github.com/graphql-rust/juniper/releases/tag/{{crate_name}}-{{version}})"},
 ]
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 05a90675..7aaed6e0 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -1,4 +1,8 @@
 jobs:
+  ###################################################
+  # Formatting
+  ###################################################
+
   - job: check_formatting
     displayName: Check formatting
     pool:
@@ -12,6 +16,10 @@ jobs:
           $HOME/.cargo/bin/cargo fmt -- --check
         displayName: Run rustfmt
 
+  ###################################################
+  # Book
+  ###################################################
+
   - job: run_book_tests
     displayName: Book code example tests
     pool:
@@ -42,6 +50,10 @@ jobs:
       - script: |
           ./docs/book/ci-build.sh master
 
+  ###################################################
+  # Main Builds
+  ###################################################
+
   - template: _build/azure-pipelines-template.yml
     parameters:
       name: Linux
@@ -56,3 +68,38 @@ jobs:
     parameters:
       name: Windows
       vmImage: vs2017-win2016
+
+  ###################################################
+  # Releases
+  ###################################################
+
+  - job: check_release_automation
+    displayName: Check release automation
+    pool:
+      vmImage: ubuntu-16.04
+    steps:
+      - script: |
+          curl https://sh.rustup.rs -sSf | sh -s -- -y
+        displayName: Install stable Rust
+      - script: |
+          _build/cargo-make.sh "0.19.1" "x86_64-unknown-linux-musl"
+        displayName: Install cargo-make binary
+      - script: |
+          $HOME/.cargo/bin/cargo install cargo-release
+        displayName: Install cargo-release binary
+      - script: |
+          git config --local user.name "Release Test Bot"
+          git config --local user.email "juniper@example.com"
+        displayName: Set up git
+      - script: |
+          RELEASE_LEVEL="minor" "$HOME/.cargo/bin/cargo" make release-dry-run
+        displayName: Dry run mode
+      - script: |
+          RELEASE_LEVEL="minor" "$HOME/.cargo/bin/cargo" make release-local-test
+        displayName: Local test mode
+      - script: |
+          git --no-pager log -p HEAD...HEAD~20
+        displayName: Echo local changes
+      - script: |
+          cargo make workspace-ci-flow --no-workspace
+        displayName: Make sure build and tests still work
diff --git a/integration_tests/juniper_tests/Makefile.toml b/integration_tests/juniper_tests/Makefile.toml
index 8c64aa89..814cb281 100644
--- a/integration_tests/juniper_tests/Makefile.toml
+++ b/integration_tests/juniper_tests/Makefile.toml
@@ -7,9 +7,13 @@ args = ["test", "--verbose"]
 
 [tasks.release]
 disabled = true
-
-[tasks.release-patch]
+[tasks.release-some]
+disabled = true
+[tasks.release-local-test]
+disabled = true
+[tasks.release-some-local-test]
 disabled = true
-
 [tasks.release-dry-run]
 disabled = true
+[tasks.release-some-dry-run]
+disabled = true
diff --git a/juniper/Makefile.toml b/juniper/Makefile.toml
index 07a88af4..886e8abf 100644
--- a/juniper/Makefile.toml
+++ b/juniper/Makefile.toml
@@ -1,8 +1,11 @@
-[tasks.release]
-args = ["release"]
+# This is needed as the release config is at a different path than the top-level
+# release config.
 
-[tasks.release-patch]
-args = ["release", "patch"]
+[tasks.release-INTERNAL]
+args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/release.toml", "${RELEASE_LEVEL}"]
 
-[tasks.release-dry-run]
-args = ["release", "--dry-run"]
+[tasks.release-dry-run-INTERNAL]
+args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/release.toml", "--dry-run", "${RELEASE_LEVEL}"]
+
+[tasks.release-local-test-INTERNAL]
+args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/release.toml", "--no-confirm", "--skip-publish", "--skip-push", "--skip-tag", "${RELEASE_LEVEL}"]
diff --git a/juniper/release.toml b/juniper/release.toml
index 97144075..689c2096 100644
--- a/juniper/release.toml
+++ b/juniper/release.toml
@@ -4,12 +4,21 @@ pro-release-commit-message = "Bump {{crate_name}} version to {{next_version}}"
 tag-message = "Release {{crate_name}} {{version}}"
 upload-doc = false
 pre-release-replacements = [
+  # Juniper's changelog
   {file="CHANGELOG.md", search="# master", replace="# master\n\n- No changes yet\n\n# [[{{version}}] {{date}}](https://github.com/graphql-rust/juniper/releases/tag/{{crate_name}}-{{version}})"},
-  {file="../integration_tests/juniper_tests/Cargo.toml", search="juniper = { version = \"0.11.0\"", replace="juniper = { version = \"{{version}}\""},
-  {file="../juniper_hyper/Cargo.toml", search="juniper = { version = \"0.11.0\"", replace="juniper = { version = \"{{version}}\""},
-  {file="../juniper_iron/Cargo.toml", search="juniper = { version = \"0.11.0\"", replace="juniper = { version = \"{{version}}\""},
-  {file="../juniper_rocket/Cargo.toml", search="juniper = { version = \"0.11.0\"", replace="juniper = { version = \"{{version}}\""},
-  {file="../juniper_tests/Cargo.toml", search="juniper = { version = \"0.11.0\"", replace="juniper = { version = \"{{version}}\""},
-  {file="../juniper_warp/Cargo.toml", search="juniper = { version = \"0.11.0\"", replace="juniper = { version = \"{{version}}\""},
-  {file="release.toml", search="0.11.0", replace="{{version}}"},
+  # Tests.
+  {file="../integration_tests/juniper_tests/Cargo.toml", search="juniper = \\{ version = \"[^\"]+\"", replace="juniper = { version = \"{{version}}\""},
+  # Hyper
+  {file="../juniper_hyper/Cargo.toml", search="juniper = \\{ version = \"[^\"]+\"", replace="juniper = { version = \"{{version}}\""},
+  {file="../juniper_hyper/Cargo.toml", search="\\[dev-dependencies\\.juniper\\]\nversion = \"[^\"]+\"", replace="[dev-dependencies.juniper]\nversion = \"{{version}}\""},
+  # Iron
+  {file="../juniper_iron/Cargo.toml", search="juniper = \\{ version = \"[^\"]+\"", replace="juniper = { version = \"{{version}}\""},
+  {file="../juniper_iron/Cargo.toml", search="\\[dev-dependencies\\.juniper\\]\nversion = \"[^\"]+\"", replace="[dev-dependencies.juniper]\nversion = \"{{version}}\""},
+  # Rocket
+  {file="../juniper_rocket/Cargo.toml", search="juniper = \\{ version = \"[^\"]+\"", replace="juniper = { version = \"{{version}}\""},
+  {file="../juniper_rocket/Cargo.toml", search="\\[dev-dependencies\\.juniper\\]\nversion = \"[^\"]+\"", replace="[dev-dependencies.juniper]\nversion = \"{{version}}\""},
+  # Warp
+  {file="../juniper_warp/Cargo.toml", search="juniper = \\{ version = \"[^\"]+\"", replace="juniper = { version = \"{{version}}\""},
+  {file="../juniper_warp/Cargo.toml", search="\\[dev-dependencies\\.juniper\\]\nversion = \"[^\"]+\"", replace="[dev-dependencies.juniper]\nversion = \"{{version}}\""},
+
 ]
diff --git a/juniper_codegen/Makefile.toml b/juniper_codegen/Makefile.toml
index 07a88af4..886e8abf 100644
--- a/juniper_codegen/Makefile.toml
+++ b/juniper_codegen/Makefile.toml
@@ -1,8 +1,11 @@
-[tasks.release]
-args = ["release"]
+# This is needed as the release config is at a different path than the top-level
+# release config.
 
-[tasks.release-patch]
-args = ["release", "patch"]
+[tasks.release-INTERNAL]
+args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/release.toml", "${RELEASE_LEVEL}"]
 
-[tasks.release-dry-run]
-args = ["release", "--dry-run"]
+[tasks.release-dry-run-INTERNAL]
+args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/release.toml", "--dry-run", "${RELEASE_LEVEL}"]
+
+[tasks.release-local-test-INTERNAL]
+args = ["release", "--config", "${CARGO_MAKE_WORKING_DIRECTORY}/release.toml", "--no-confirm", "--skip-publish", "--skip-push", "--skip-tag", "${RELEASE_LEVEL}"]
diff --git a/juniper_codegen/release.toml b/juniper_codegen/release.toml
index 8c0f4d0a..f6a76a92 100644
--- a/juniper_codegen/release.toml
+++ b/juniper_codegen/release.toml
@@ -4,6 +4,5 @@ pro-release-commit-message = "Bump {{crate_name}} version to {{next_version}}"
 tag-message = "Release {{crate_name}} {{version}}"
 upload-doc = false
 pre-release-replacements = [
-  {file="../juniper/Cargo.toml", search="juniper_codegen = { version = \"0.11.0\"", replace="juniper_codegen = { version = \"{{version}}\""},
-  {file="release.toml", search="0.11.0", replace="{{version}}"},
+  {file="../juniper/Cargo.toml", search="juniper_codegen = \\{ version = \"[^\"]+\"", replace="juniper_codegen = { version = \"{{version}}\""},
 ]
diff --git a/juniper_hyper/CHANGELOG.md b/juniper_hyper/CHANGELOG.md
index f3fa8b0c..f6cd641a 100644
--- a/juniper_hyper/CHANGELOG.md
+++ b/juniper_hyper/CHANGELOG.md
@@ -1,5 +1,7 @@
 # master
 
+- Compatibility with the latest `juniper`.
+
 # 0.2.0 [2018-12-18]
 
 ## Breaking changes
diff --git a/juniper_iron/CHANGELOG.md b/juniper_iron/CHANGELOG.md
index f0b36d41..713993e4 100644
--- a/juniper_iron/CHANGELOG.md
+++ b/juniper_iron/CHANGELOG.md
@@ -1,6 +1,6 @@
 # master
 
-- No changes yet
+- Compatibility with the latest `juniper`.
 
 # [0.3.0] 2018-12-17
 
diff --git a/juniper_rocket/CHANGELOG.md b/juniper_rocket/CHANGELOG.md
index ae8b5ebb..a2890b19 100644
--- a/juniper_rocket/CHANGELOG.md
+++ b/juniper_rocket/CHANGELOG.md
@@ -1,6 +1,6 @@
 # master
 
-- No changes yet
+- Compatibility with the latest `juniper`.
 
 # [0.2.0] 2018-12-17
 
diff --git a/juniper_warp/CHANGELOG.md b/juniper_warp/CHANGELOG.md
index 00998ec9..277fde70 100644
--- a/juniper_warp/CHANGELOG.md
+++ b/juniper_warp/CHANGELOG.md
@@ -1,6 +1,6 @@
 # master
 
-- No changes yet
+- Compatibility with the latest `juniper`.
 
 # [0.2.0] 2018-12-17
 
diff --git a/juniper_warp/Cargo.toml b/juniper_warp/Cargo.toml
index d35c7837..de3d05d9 100644
--- a/juniper_warp/Cargo.toml
+++ b/juniper_warp/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
 
 [dependencies]
 warp = "0.1.8"
-juniper = { path = "../juniper", version = "0.11.0", default-features = false  }
+juniper = { version = "0.11.0", path = "../juniper", default-features = false  }
 serde_json = "1.0.24"
 serde_derive = "1.0.75"
 failure = "0.1.2"
@@ -19,7 +19,7 @@ serde = "1.0.75"
 tokio-threadpool = "0.1.7"
 
 [dev-dependencies]
-juniper = { path = "../juniper", version = "0.11.0", features = ["expose-test-schema", "serde_json"] }
+juniper = { version = "0.11.0", path = "../juniper", features = ["expose-test-schema", "serde_json"] }
 env_logger = "0.5.11"
 log = "0.4.3"
 percent-encoding = "1.0"