diff --git a/docs/en/04_Changelogs/4.0.0.md b/docs/en/04_Changelogs/4.0.0.md
index c47e156a9..e5f9a806a 100644
--- a/docs/en/04_Changelogs/4.0.0.md
+++ b/docs/en/04_Changelogs/4.0.0.md
@@ -16,7 +16,6 @@ guide developers in preparing existing 3.x code for compatibility with 4.0
* [Filesystem API](#overview-filesystem)
* [Template and Form API](#overview-template)
* [i18n](#overview-i18n)
- * [Cache](#overview-cache)
* [Email and Mailer](#overview-mailer)
* [SapphireTest](#overview-testing)
* [Commit History](#commit-history)
@@ -1034,6 +1033,108 @@ One removed feature is the `Config::FIRST_SET` option. Either use uninherited co
directly, or use the inherited config lookup. As falsey values now overwrite all parent class values, it is
now generally safer to use the default inherited config, where in the past you would need to use `FIRST_SET`.
+#### Upgrading Cache API
+
+We have replaced the unsupported `Zend_Cache` library with [symfony/cache](https://github.com/symfony/cache).
+This also allowed us to remove SilverStripe's `Cache` API and use dependency injection with a standard
+[PSR-16](http://www.php-fig.org/psr/psr-16/) cache interface instead.
+
+Caches should be retrieved through `Injector` instead of `Cache::factory()`,
+and have a slightly different API (e.g. `set()` instead of `save()`).
+
+Before:
+
+
+ :::php
+ $cache = Cache::factory('myCache');
+
+ // create a new item by trying to get it from the cache
+ $myValue = $cache->load('myCacheKey');
+
+ // set a value and save it via the adapter
+ $cache->save(1234, 'myCacheKey');
+
+ // retrieve the cache item
+ if (!$cache->load('myCacheKey')) {
+ // ... item does not exists in the cache
+ }
+
+ // Remove a cache key
+ $cache->remove('myCacheKey');
+
+
+After:
+
+
+ :::php
+ use Psr\SimpleCache\CacheInterface;
+ $cache = Injector::inst()->get(CacheInterface::class . '.myCache');
+
+ // create a new item by trying to get it from the cache
+ $myValue = $cache->get('myCacheKey');
+
+ // set a value and save it via the adapter
+ $cache->set('myCacheKey', 1234);
+
+ // retrieve the cache item
+ if (!$cache->has('myCacheKey')) {
+ // ... item does not exists in the cache
+ }
+
+ $cache->delete('myCacheKey');
+
+
+With the necessary minimal config in `_config/mycache.yml`
+
+
+ :::yml
+ ---
+ Name: mycache
+ ---
+ SilverStripe\Core\Injector\Injector:
+ Psr\SimpleCache\CacheInterface.myCache:
+ factory: SilverStripe\Core\Cache\CacheFactory
+ constructor:
+ namespace: 'mycache'
+
+
+##### Configuration Changes
+
+Caches are now configured through dependency injection services instead of PHP.
+See our ["Caching" docs](/developer-guides/performance/caching) for more details.
+
+Before (`mysite/_config.php`):
+
+ :::php
+ Cache::add_backend(
+ 'primary_memcached',
+ 'Memcached',
+ array(
+ 'servers' => array(
+ 'host' => 'localhost',
+ 'port' => 11211,
+ )
+ )
+ );
+ Cache::pick_backend('primary_memcached', 'any', 10);
+
+After (`mysite/_config/config.yml`):
+
+ :::yml
+ ---
+ After:
+ - '#corecache'
+ ---
+ SilverStripe\Core\Injector\Injector:
+ MemcachedClient:
+ class: 'Memcached'
+ calls:
+ - [ addServer, [ 'localhost', 11211 ] ]
+ SilverStripe\Core\Cache\CacheFactory:
+ class: 'SilverStripe\Core\Cache\MemcachedCacheFactory'
+ constructor:
+ client: '%$MemcachedClient
+
## API Changes
@@ -1617,110 +1718,6 @@ New `TimeField` methods replace `getConfig()` / `setConfig()`
* `i18n::get_language_code()` removed.
* `i18n::get_common_locales()` removed.
* `i18n.common_locales` config removed
-
-### Cache API
-
-We have replaced the unsupported `Zend_Cache` library with [symfony/cache](https://github.com/symfony/cache).
-This also allowed us to remove SilverStripe's `Cache` API and use dependency injection with a standard
-[PSR-16](http://www.php-fig.org/psr/psr-16/) cache interface instead.
-
-#### Usage Changes
-
-Caches should be retrieved through `Injector` instead of `Cache::factory()`,
-and have a slightly different API (e.g. `set()` instead of `save()`).
-
-Before:
-
-
- :::php
- $cache = Cache::factory('myCache');
-
- // create a new item by trying to get it from the cache
- $myValue = $cache->load('myCacheKey');
-
- // set a value and save it via the adapter
- $cache->save(1234, 'myCacheKey');
-
- // retrieve the cache item
- if (!$cache->load('myCacheKey')) {
- // ... item does not exists in the cache
- }
-
- // Remove a cache key
- $cache->remove('myCacheKey');
-
-
-After:
-
-
- :::php
- use Psr\SimpleCache\CacheInterface;
- $cache = Injector::inst()->get(CacheInterface::class . '.myCache');
-
- // create a new item by trying to get it from the cache
- $myValue = $cache->get('myCacheKey');
-
- // set a value and save it via the adapter
- $cache->set('myCacheKey', 1234);
-
- // retrieve the cache item
- if (!$cache->has('myCacheKey')) {
- // ... item does not exists in the cache
- }
-
- $cache->delete('myCacheKey');
-
-
-With the necessary minimal config in `_config/mycache.yml`
-
-
- :::yml
- ---
- Name: mycache
- ---
- SilverStripe\Core\Injector\Injector:
- Psr\SimpleCache\CacheInterface.myCache:
- factory: SilverStripe\Core\Cache\CacheFactory
- constructor:
- namespace: 'mycache'
-
-
-#### Configuration Changes
-
-Caches are now configured through dependency injection services instead of PHP.
-See our ["Caching" docs](/developer-guides/performance/caching) for more details.
-
-Before (`mysite/_config.php`):
-
- :::php
- Cache::add_backend(
- 'primary_memcached',
- 'Memcached',
- array(
- 'servers' => array(
- 'host' => 'localhost',
- 'port' => 11211,
- )
- )
- );
- Cache::pick_backend('primary_memcached', 'any', 10);
-
-After (`mysite/_config/config.yml`):
-
- :::yml
- ---
- After:
- - '#corecache'
- ---
- SilverStripe\Core\Injector\Injector:
- MemcachedClient:
- class: 'Memcached'
- calls:
- - [ addServer, [ 'localhost', 11211 ] ]
- SilverStripe\Core\Cache\CacheFactory:
- class: 'SilverStripe\Core\Cache\MemcachedCacheFactory'
- constructor:
- client: '%$MemcachedClient
### Email and Mailer