diff --git a/core/lib/Drupal/Core/Config/ConfigEvents.php b/core/lib/Drupal/Core/Config/ConfigEvents.php
index 794fbc5..dd60540 100644
--- a/core/lib/Drupal/Core/Config/ConfigEvents.php
+++ b/core/lib/Drupal/Core/Config/ConfigEvents.php
@@ -21,11 +21,20 @@
    * object is saved. The event listener method receives a
    * \Drupal\Core\Config\ConfigCrudEvent instance.
    *
+   * This event will be fired when configuration is saved by hook_update_N()
+   * implementations. Subscribers to this event can not make any assumption that
+   * the config data is valid and should ensure that their actions are safe
+   * regardless of the structure of the underlying configuration. When changing
+   * configuration values, the value must be the correct data type.
+   * Configuration must be saved with the $trusted_data flag set to TRUE so that
+   * configuration schema are not used.
+   *
    * @Event
    *
    * @see \Drupal\Core\Config\ConfigCrudEvent
    * @see \Drupal\Core\Config\Config::save()
    * @see \Drupal\Core\Config\ConfigFactory::onConfigSave()
+   * @see hook_update_N()
    *
    * @var string
    */
@@ -38,11 +47,20 @@
    * object is deleted. The event listener method receives a
    * \Drupal\Core\Config\ConfigCrudEvent instance.
    *
+   * This event will be fired when configuration is deleted by hook_update_N()
+   * implementations. Subscribers to this event can not make any assumption that
+   * the config data is valid and should ensure that their actions are safe
+   * regardless of the structure of the underlying configuration. When changing
+   * configuration values, the value must be the correct data type.
+   * Configuration must be saved with the $trusted_data flag set to TRUE so that
+   * configuration schema are not used.
+   *
    * @Event
    *
    * @see \Drupal\Core\Config\ConfigCrudEvent
    * @see \Drupal\Core\Config\Config::delete()
    * @see \Drupal\Core\Config\ConfigFactory::onConfigDelete()
+   * @see hook_update_N()
    *
    * @var string
    */
@@ -55,10 +73,19 @@
    * object's name is changed. The event listener method receives a
    * \Drupal\Core\Config\ConfigRenameEvent instance.
    *
+   * This event will be fired when configuration is renamed by hook_update_N()
+   * implementations. Subscribers to this event can not make any assumption that
+   * the config data is valid and should ensure that their actions are safe
+   * regardless of the structure of the underlying configuration. When changing
+   * configuration values, the value must be the correct data type.
+   * Configuration must be saved with the $trusted_data flag set to TRUE so that
+   * configuration schema are not used.
+   *
    * @Event
    *
    * @see \Drupal\Core\Config\ConfigRenameEvent
-   * @see \Drupal\Core\Config\ConfigFactoryInterface::rename().
+   * @see \Drupal\Core\Config\ConfigFactoryInterface::rename()
+   * @see hook_update_N()
    *
    * @var string
    */
diff --git a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php
index d7bc941..2dc6caa 100644
--- a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php
+++ b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php
@@ -74,7 +74,7 @@ protected function filterOverride(Config $config, StorableConfigBase $override)
     }
     elseif ($changed) {
       // Otherwise set the filtered override values back.
-      $override->setData($override_data)->save();
+      $override->setData($override_data)->save(TRUE);
     }
   }
 
diff --git a/core/lib/Drupal/Core/Extension/module.api.php b/core/lib/Drupal/Core/Extension/module.api.php
index 63f81b2..55c2741 100644
--- a/core/lib/Drupal/Core/Extension/module.api.php
+++ b/core/lib/Drupal/Core/Extension/module.api.php
@@ -464,11 +464,25 @@ function hook_install_tasks_alter(&$tasks, $install_state) {
  * In order to call a function from your mymodule.module or an include file,
  * you need to explicitly load that file first.
  *
- * During database updates the schema of any module could be out of date. For
- * this reason, caution is needed when using any API function within an update
+ * Implementations must ensure that APIs used are safe during updates. During
+ * database updates the schema of any module could be out of date. For this
+ * reason, caution is needed when using any API function within an update
  * function - particularly CRUD functions, functions that depend on the schema
- * (for example by using drupal_write_record()), and any functions that invoke
- * hooks.
+ * (for example by using \Drupal\Core\Entity\Entity::save()), and any functions
+ * that invoke hooks.
+ *
+ * The following actions are examples that are safe:
+ * - Cache invalidation.
+ * - Using \Drupal::configFactory()->getEditable() and \Drupal::config().
+ *   Implementations can not make any assumption that the config data is valid.
+ *   Implementations must use the $trusted_data argument for
+ *   \Drupal\Core\Config\Config::save() and pass schema correct data so that
+ *   configuration schema are not used whilst saving configuration.
+ * - Marking a container for rebuild.
+ *
+ * The following actions are examples that are unsafe:
+ * - Calling Node::save().
+ * - Rebuilding the router using \Drupal::service('router.builder')->rebuild().
  *
  * The $sandbox parameter should be used when a multipass update is needed, in
  * circumstances where running the whole update at once could cause PHP to
diff --git a/core/modules/language/language.services.yml b/core/modules/language/language.services.yml
index b9de79a..fc9bba1 100644
--- a/core/modules/language/language.services.yml
+++ b/core/modules/language/language.services.yml
@@ -9,7 +9,7 @@ services:
       - [initLanguageManager]
   language.config_subscriber:
     class: Drupal\language\EventSubscriber\ConfigSubscriber
-    arguments: ['@language_manager', '@language.default']
+    arguments: ['@language_manager', '@language.default', '@config.factory']
     tags:
       - { name: event_subscriber }
   language.config_factory_override:
diff --git a/core/modules/language/src/EventSubscriber/ConfigSubscriber.php b/core/modules/language/src/EventSubscriber/ConfigSubscriber.php
index d3486aa..4f087e3 100644
--- a/core/modules/language/src/EventSubscriber/ConfigSubscriber.php
+++ b/core/modules/language/src/EventSubscriber/ConfigSubscriber.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\language\EventSubscriber;
 
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Language\Language;
 use Drupal\Core\Language\LanguageDefault;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Config\ConfigCrudEvent;
@@ -34,16 +36,26 @@ class ConfigSubscriber implements EventSubscriberInterface {
   protected $languageDefault;
 
   /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
    * Constructs a new class object.
    *
    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
    * @param \Drupal\Core\Language\LanguageDefault $language_default
    *   The default language.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The configuration factory.
    */
-  public function __construct(LanguageManagerInterface $language_manager, LanguageDefault $language_default) {
+  public function __construct(LanguageManagerInterface $language_manager, LanguageDefault $language_default, ConfigFactoryInterface $config_factory) {
     $this->languageManager = $language_manager;
     $this->languageDefault = $language_default;
+    $this->configFactory = $config_factory;
   }
 
   /**
@@ -55,12 +67,31 @@ public function __construct(LanguageManagerInterface $language_manager, Language
   public function onConfigSave(ConfigCrudEvent $event) {
     $saved_config = $event->getConfig();
     if ($saved_config->getName() == 'system.site' && $event->isChanged('default_langcode')) {
-      $language = $this->languageManager->getLanguage($saved_config->get('default_langcode'));
+      $new_default_langcode = $saved_config->get('default_langcode');
+
+      $default_language = $this->configFactory->get('language.entity.' . $new_default_langcode);
       // During an import the language might not exist yet.
-      if ($language) {
-        $this->languageDefault->set($language);
+      if (!$default_language->isNew()) {
+        $old_default_langcode = $saved_config->getOriginal('default_langcode');
+        $this->languageDefault->set(new Language($default_language->get()));
         $this->languageManager->reset();
-        language_negotiation_url_prefixes_update();
+
+        // Directly update language negotiation settings instead of calling
+        // language_negotiation_url_prefixes_update().
+        $negotiation_config = $this->configFactory->getEditable('language.negotiation');
+        $negotiation_changed = FALSE;
+        $url_prefixes = $negotiation_config->get('url.prefixes');
+        if (empty($url_prefixes[$old_default_langcode])) {
+          $negotiation_config->set('url.prefixes.' . $old_default_langcode, $old_default_langcode);
+          $negotiation_changed = TRUE;
+        }
+        if (empty($url_prefixes[$new_default_langcode])) {
+          $negotiation_config->set('url.prefixes.' . $new_default_langcode, '');
+          $negotiation_changed = TRUE;
+        }
+        if ($negotiation_changed) {
+          $negotiation_config->save(TRUE);
+        }
       }
       // Trigger a container rebuild on the next request by invalidating it.
       ConfigurableLanguageManager::rebuildServices();
