diff --git a/.airtap.yml b/.airtap.yml
index 9ffbfa0..979fbf8 100644
--- a/.airtap.yml
+++ b/.airtap.yml
@@ -6,7 +6,7 @@ browsers:
   - name: safari
     version: latest
   - name: ie
-    version: latest
+    version: '11'
   - name: microsoftedge
     version: latest
   - name: iphone
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 05e8c9e..e39a524 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,42 @@
 # Changelog
 
+_**If you are upgrading:** please see [`UPGRADING.md`](UPGRADING.md)._
+
 ## [Unreleased][unreleased]
 
+## [4.0.0] - 2019-03-29
+
+### Changed
+
+- Upgrade `abstract-leveldown` from `~5.0.0` to `~6.0.1` ([#174](https://github.com/Level/memdown/issues/174)) ([**@vweevers**](https://github.com/vweevers))
+- Invoke abstract tests from single function ([#174](https://github.com/Level/memdown/issues/174)) ([**@vweevers**](https://github.com/vweevers))
+- Use `level-concat-iterator` and `testCommon.factory()` in custom tests ([#174](https://github.com/Level/memdown/issues/174)) ([**@vweevers**](https://github.com/vweevers))
+- Don't use falsy or undefined as not-defined signal ([#174](https://github.com/Level/memdown/issues/174)) ([**@vweevers**](https://github.com/vweevers))
+  ([**@vweevers**](https://github.com/vweevers))
+- Upgrade `standard` devDependency from `^11.0.0` to `^12.0.0` ([#173](https://github.com/Level/memdown/issues/173)) ([**@ralphtheninja**](https://github.com/ralphtheninja))
+- Upgrade `airtap` devDependency from `^0.0.5` to `^2.0.0` ([#161](https://github.com/Level/memdown/issues/161), [#163](https://github.com/Level/memdown/issues/163), [#168](https://github.com/Level/memdown/issues/168), [#172](https://github.com/Level/memdown/issues/172), [#177](https://github.com/Level/memdown/issues/177)) ([**@vweevers**](https://github.com/vweevers))
+- Tweak copyright years for less maintenance ([`760b375`](https://github.com/Level/memdown/commit/760b375)) ([**@ralphtheninja**](https://github.com/ralphtheninja))
+- Apply common project tweaks ([#178](https://github.com/Level/memdown/issues/178), [#179](https://github.com/Level/memdown/issues/179)) ([**@vweevers**](https://github.com/vweevers))
+
+### Added
+
+- Add `nyc` and browser code coverage ([#169](https://github.com/Level/memdown/issues/169), [#180](https://github.com/Level/memdown/issues/180)) ([**@vweevers**](https://github.com/vweevers))
+- Add Sauce Labs logo to README ([#165](https://github.com/Level/memdown/issues/165)) ([**@ralphtheninja**](https://github.com/ralphtheninja))
+- Document that nullish values are now also rejected ([#174](https://github.com/Level/memdown/issues/174)) ([**@vweevers**](https://github.com/vweevers))
+- Test negative and positive `Infinity` as keys ([#174](https://github.com/Level/memdown/issues/174)) ([**@vweevers**](https://github.com/vweevers))
+
+### Removed
+
+- Remove contributors from `package.json` ([`80b3e3a`](https://github.com/Level/memdown/commit/80b3e3a)) ([**@ralphtheninja**](https://github.com/ralphtheninja))
+- Remove node 9 ([`0de8721`](https://github.com/Level/memdown/commit/0de8721)) ([**@ralphtheninja**](https://github.com/ralphtheninja))
+- Remove now irrelevant serialization of nullish values ([#174](https://github.com/Level/memdown/issues/174)) ([**@vweevers**](https://github.com/vweevers))
+- Remove dummy location from `abstract-leveldown` constructor call ([#174](https://github.com/Level/memdown/issues/174)) ([**@vweevers**](https://github.com/vweevers))
+- Remove `rimraf` devDependency ([#174](https://github.com/Level/memdown/issues/174)) ([**@vweevers**](https://github.com/vweevers))
+
+### Fixed
+
+- Fix link references in `UPGRADING.md` for latest `remark` ([`f111a6f`](https://github.com/Level/memdown/commit/f111a6f)) ([**@vweevers**](https://github.com/vweevers))
+
 ## [3.0.0] - 2018-05-22
 
 ### Added
@@ -69,7 +104,9 @@
 - Remove unnecessary `rimraf` replacement for Browserify ([**@vweevers**](https://github.com/vweevers))
 - README: remove Greenkeeper badge ([**@ralphtheninja**](https://github.com/ralphtheninja))
 
-[unreleased]: https://github.com/level/memdown/compare/v3.0.0...HEAD
+[unreleased]: https://github.com/level/memdown/compare/v4.0.0...HEAD
+
+[4.0.0]: https://github.com/level/memdown/compare/v3.0.0...v4.0.0
 
 [3.0.0]: https://github.com/level/memdown/compare/v2.0.0...v3.0.0
 
diff --git a/README.md b/README.md
index 05fd080..fc599dc 100644
--- a/README.md
+++ b/README.md
@@ -38,8 +38,6 @@ Your data is discarded when the process ends or you release a reference to the s
 
 [![Sauce Test Status](https://saucelabs.com/browser-matrix/level-ci.svg)](https://saucelabs.com/u/level-ci)
 
-`memdown` requires a ES5-capable browser. If you're using one that's isn't (e.g. PhantomJS, Android < 4.4, IE < 10) then you will need [es5-shim](https://github.com/es-shims/es5-shim).
-
 ## Data types
 
 Unlike [`leveldown`], `memdown` does not stringify keys or values. This means that in addition to Buffers, you can store any JS type without the need for [`encoding-down`]. For keys for example, you could use Buffers or strings, which sort lexicographically, or numbers, even Dates, which sort naturally. The only exceptions are `null` and `undefined`. Keys and values of that type are rejected.
diff --git a/UPGRADING.md b/UPGRADING.md
index a64869c..f196bb6 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -2,6 +2,32 @@
 
 This document describes breaking changes and how to upgrade. For a complete list of changes including minor and patch releases, please refer to the [`CHANGELOG`][changelog].
 
+## v4
+
+This is an upgrade to `abstract-leveldown@6` which solves long-standing issues around serialization and type support.
+
+### Range options are now serialized
+
+Previously, range options like `lt` were passed through as-is by `abstract-leveldown`, unlike keys. This makes no difference for `memdown` as it does not serialize anything.
+
+### The rules for range options have been relaxed
+
+Because `null`, `undefined`, zero-length strings and zero-length buffers are significant types in encodings like `bytewise` and `charwise`, they became valid as range options in `abstract-leveldown`. This means `db.iterator({ gt: undefined })` is not the same as `db.iterator({})`.
+
+For `memdown`, when used by itself, the behavior of `null`, `undefined`, zero-length strings and zero-length buffers is undefined.
+
+### Nullish values are rejected
+
+In addition to rejecting `null` and `undefined` as _keys_, `abstract-leveldown` now also rejects these types as _values_, due to preexisting significance in streams and iterators.
+
+### Zero-length array keys are rejected
+
+Though this was already the case, `abstract-leveldown` has replaced the behavior with an explicit `Array.isArray()` check and a new error message.
+
+### Browser support
+
+IE10 has been dropped.
+
 ## v3
 
 Dropped support for node 4. No other breaking changes.