---
name: preference-deprecation
description: >-
Safely deprecate, clean up, or migrate Chrome browser and local state
preferences following Chromium Prefs owners' guidelines across Desktop,
Android, and iOS. Use when removing, deprecating, or migrating an obsolete
Chromium preference.
---
# Preference Deprecation and Cleanup Workflow
Use this skill when asked to deprecate, clean up, remove, or migrate an obsolete
Chromium preference (Profile Pref or Local State Pref).
## Core Principles & Overview
When removing a Chromium preference from feature code, **never simply delete
registration calls or constants from feature code without migrating to a
delete-self state**. Preferences saved on disk in User Pref JSON files must be
explicitly cleaned up to prevent leaving abandoned, unread data in user profiles
indefinitely.
Deleted preferences must remain in a "delete-self" state for **1 year** before
the cleanup logic itself is pruned.
______________________________________________________________________
## Step-by-Step Execution Checklist
### Step 1: Audit Preference Properties & Scope
1. **Scope Check**: Determine if the pref is a **Profile Pref**
(`Profile::GetPrefs()`) or **Local State Pref**
(`g_browser_process->local_state()`).
2. **Platform & Embedder Scope Check**: Determine which platforms and embedders
register and use the preference (e.g., Desktop, Android, iOS, ChromeOS, or
shared `components/`). This determines which platform migration hubs will
require delete-self cleanup in Step 3.
3. **Syncable Pref Check**: Check if the pref had any `SYNCABLE_*` flag (such as
`user_prefs::PrefRegistrySyncable::SYNCABLE_PREF`, `SYNCABLE_PRIORITY_PREF`,
or the ChromeOS-only counterparts `SYNCABLE_OS_PREF` /
`SYNCABLE_OS_PRIORITY_PREF`) or was present in a syncable prefs database:
- `components/sync_preferences/common_syncable_prefs_database.cc`
- `chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc`
- `ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database.cc`
- **Syncable Database Handling**:
- **Reserved IDs**: In `syncable_prefs_ids` inside the database `.cc` file
(e.g., `common_syncable_prefs_database.cc`), **do NOT delete** the ID
constant. Instead, **comment out the entry** so the numeric ID remains
reserved (e.g. `// kMyPref = 123, (deprecated)`).
- **Remove from Allowlist Map**: **Delete** the entry completely from the
corresponding allowlist map (e.g., `kCommonSyncablePrefsAllowlist`,
`kChromeSyncablePrefsAllowlist`, or `kIOSChromeSyncablePrefsAllowlist`).
- **Histogram Enum & Presubmit Check**: Add `(obsolete)` to the
corresponding entry label in
`tools/metrics/histograms/metadata/sync/enums.xml` (within the
`SyncablePref` enum, e.g.,
``), satisfying the
change coupling presubmit check (`CommonSyncablePref`,
`ChromeSyncablePref`, or `IosSyncablePref`).
- **Registration Flags**: Remove any `SYNCABLE_*` flag from any active
registrations.
4. **Policy Check**: Check if the pref is backed by an Enterprise Policy.
- **Policy-Only Exception**: If the pref was *only* exposed via Enterprise
Policy in `Managed Prefs` (with no UI allowing end-users to change it), it
was never written to User Prefs JSON on disk. Migration/`ClearPref()` in
`browser_prefs.cc` is **not required**.
- If the policy pref was user-modifiable, follow
`docs/enterprise/add_new_policy.md` to deprecate the policy for several
milestones *before* removing pref logic.
______________________________________________________________________
### Step 2: Code Cleanup in Feature Code
1. Delete call sites that read/write the obsolete pref from feature logic and UI
code (including Android Java callsites such as `Pref.java`, `PrefNames.java`,
or `ChromePreferenceKeys` where applicable).
2. Remove pref name declarations/definitions from their original locations
(common locations include component pref headers such as
`components//.../pref_names.h`, feature headers like
`chrome/browser//..._prefs.h`, `chrome/common/pref_names.h`,
`ash/constants/ash_pref_names.h`, `ios/chrome/.../pref_names.h`, or local
`.cc`/`.h` files).
3. Remove the registration call from the component's `RegisterProfilePrefs()` or
`RegisterLocalState()` method.
4. Update or remove feature unit tests (e.g. `..._unittest.cc` and Android unit
or instrumentation tests) that asserted pref registration, default values, or
syncability.
______________________________________________________________________
### Step 3: Add Delete-Self Registration & Cleanup / Migration Logic
When removing a pref from feature code, the raw string constant **must be
relocated/added into the migration files** (`browser_prefs.cc` /
`browser_prefs.mm`) so `browser_prefs` can clear leftover disk data.
#### A. Desktop / Android / ChromeOS (`chrome/browser/prefs/browser_prefs.cc`)
If the pref was registered on Desktop, Android, or ChromeOS (either shared
across platforms or exclusive to any of them):
1. **Chronological Ordering (Primary Invariant - Year then Month)**:
- All deprecation registrations and `ClearPref()` calls **must be ordered
chronologically by date (`MM/YYYY`)**.
- **Respect Year Ordering**: When sorting entries, ensure the full date is
considered (e.g., `08/2026` must be placed **after** `11/2025` and
`07/2026`, never sorted alphabetically by month alone).
- **Respect PRESUBMIT Markers**:
- **Profile Prefs**:
- Register in `RegisterProfilePrefsForMigration()`.
- Clear/migrate in `MigrateObsoleteProfilePrefs()` within the
`// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS` and
`// END_MIGRATE_OBSOLETE_PROFILE_PREFS` markers.
- **Local State Prefs**:
- Register in `RegisterLocalStatePrefsForMigration()`.
- Clear/migrate in `MigrateObsoleteLocalStatePrefs()` within the
`// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS` and
`// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS` markers.
- **Never delete or modify the marker lines** as they are enforced by
presubmit checks (`PRESUBMIT.py`).
2. **Relocate Constant**: Move the pref name string constant to the anonymous
namespace of `chrome/browser/prefs/browser_prefs.cc` under
`// Deprecated MM/YYYY.`:
```cpp
// Deprecated MM/YYYY.
constexpr char kObsoletePrefName[] = "path.to.obsolete_pref";
```
3. **Register Obsolete Pref**:
- Register the pref in `RegisterProfilePrefsForMigration()` (Profile Prefs)
or `RegisterLocalStatePrefsForMigration()` (Local State Prefs).
- **MUST match the original pref type** (Boolean, Integer, String, List,
Dictionary, Time, etc.) without any `SYNCABLE_*` flags (e.g.
`SYNCABLE_PREF`, `SYNCABLE_PRIORITY_PREF`, `SYNCABLE_OS_PREF`,
`SYNCABLE_OS_PRIORITY_PREF`):
```cpp
// Deprecated MM/YYYY.
registry->RegisterBooleanPref(kObsoletePrefName, false);
registry->RegisterTimePref(kObsoleteTimePrefName, base::Time());
registry->RegisterDictionaryPref(kObsoleteDictPrefName);
```
4. **Clear Leftover Data or Migrate**:
- For **Deletion**: Add `profile_prefs->ClearPref()` in
`MigrateObsoleteProfilePrefs()` or `local_state->ClearPref()` in
`MigrateObsoleteLocalStatePrefs()` with the matching date comment:
```cpp
// Added MM/YYYY.
profile_prefs->ClearPref(kObsoletePrefName);
```
- For **Migration** (moving old pref value to a new key): Use
`GetUserPrefValue()` (NOT `Get*()`) to avoid migrating default/policy
values:
```cpp
// Added MM/YYYY.
const base::Value* value =
profile_prefs->GetUserPrefValue(kObsoletePrefName);
if (value) {
profile_prefs->Set(kNewPrefName, *value);
profile_prefs->ClearPref(kObsoletePrefName);
}
```
#### B. iOS Chrome (`ios/chrome/browser/shared/model/prefs/browser_prefs.mm`)
If the pref was registered on iOS Chrome (either shared across platforms or
exclusive to iOS):
1. **Chronological Ordering**:
- Maintain chronological date ordering (`MM/YYYY` respecting year), placing
new deprecations under the latest date block in `RegisterProfilePrefs()`,
`RegisterLocalStatePrefs()`, `MigrateObsoleteProfilePrefs()`, and
`MigrateObsoleteLocalStatePrefs()`.
2. **Relocate Constant**: Prefer defining the pref name string constant as
`constexpr char` (or `inline constexpr char`) in the anonymous
`namespace { ... }` of `browser_prefs.mm` under `// Deprecated MM/YYYY.`:
```cpp
// Deprecated MM/YYYY.
constexpr char kObsoletePrefName[] = "path.to.obsolete_pref";
```
3. **Register Obsolete Pref**: Add registration under `// Deprecated MM/YYYY.`
in `RegisterProfilePrefs()` or `RegisterLocalStatePrefs()` matching the
original type (without sync flags):
```mm
// Deprecated MM/YYYY.
registry->RegisterBooleanPref(kObsoletePrefName, false);
```
4. **Clear Leftover Data**: Add `prefs->ClearPref()` under `// Added MM/YYYY.`
in `MigrateObsoleteProfilePrefs()` or `MigrateObsoleteLocalStatePrefs()`:
```mm
// Added MM/YYYY.
prefs->ClearPref(kObsoletePrefName);
```
5. **iOS Helpers & NSUserDefaults Cleanup**:
- Use iOS helper functions like `RenameBooleanPref()` where applicable.
- If applicable, remove iOS-specific user defaults in
`MigrateObsoleteUserDefault()`.
______________________________________________________________________
### Step 4: 1+ Year Old Migration Pruning (Independent Maintenance)
Per `chrome/browser/prefs/README.md`, pruning 1+ year old migrations is an
independent maintenance task and must NOT be bundled into a feature deprecation
change.
1. **When Deprecating a Preference**:
- Do not prune older migrations in the same change.
- If you observe entries in `browser_prefs.cc` or `browser_prefs.mm` with
dates 1+ years old, optionally note them in your final summary to the user
as candidates for an independent follow-up cleanup patch.
2. **When Explicitly Asked to Prune Obsolete Migrations**:
- Locate `ClearPref()` calls and migration registrations in
`browser_prefs.cc` and `browser_prefs.mm` commented with dates 1+ years
old.
- **Check for Retention Exceptions**: Inspect surrounding comments before
removing. Do NOT remove migrations annotated with explicit retention
exceptions (e.g.,
`// Added MM/YYYY, but DO NOT REMOVE after the usual year`) or custom
migration delegates (e.g., `MigrateDeprecatedAutofillPrefs()`).
- Safely remove the constant, registration, and `ClearPref()` call.
______________________________________________________________________
### Step 5: Verification, Self-Review & Formatting
1. **Code Formatting & Verification**:
- Format code using `git cl format`.
- Build and verify affected test targets locally or run presubmit checks
(`git cl presubmit -u`) to ensure no regressions.
- If syncable preferences or `enums.xml` were modified, verify syncable prefs
unit tests (e.g.,
`unit_tests --gtest_filter="*SyncablePrefsDatabaseTest*"`).
2. **Commit Description & Trailer Formatting**:
- **Subject Prefix Conventions**: Follow the contributor's established prefix
convention (e.g., `[component]` or `component:`, checking existing CL
history or team preferences).
- **Focus on Rationale & Architectural Impact**: Write a descriptive commit
message that explains the **why**, **what it achieves**, and any
non-obvious context (e.g., why an obsolete migration lingered, memory or
performance tradeoffs, disk state cleanup invariants) rather than merely
restating the code delta.
- **Contiguous Trailer Formatting**: All metadata tags (`Bug:`, `Fixed:`,
`TAG=`, `CONV=`, `Change-Id:`) must be placed in a single contiguous
trailer block at the very bottom of the description without intervening
blank lines.
- Use `Bug: ` for incremental changes on umbrella/tracking bugs, and
`Fixed: ` only when the change fully resolves the underlying issue.
3. **Fresh-Eye Self-Review**:
- Before completing the work, perform a thorough, fresh-eye code quality
self-review to audit for cosmetic flaws, unintended diffs, redundant
headers, and description formatting.