Skip to main content
Experimental flags control the availability of new Dart language features during development. This guide is for SDK developers working on implementing and retiring these flags.
For information about using experimental flags as a Dart developer, see the experiment flags documentation.

Overview

Experimental flags allow new features to be:
  • Developed and tested while disabled by default
  • Gradually rolled out to users through beta releases
  • Enabled by default once stable
  • Retired after proven stable in production

Flag Definition

All experimental flags are defined in:
tools/experimental_features.yaml
This YAML file specifies:
  • Flag names and descriptions
  • SDK version requirements
  • Default enabled/disabled status
  • Expiration status

Feature Lifecycle

The typical lifecycle of an experimental feature spans multiple SDK versions:
1

Development phase (Version n-1)

  • Feature is developed behind a flag
  • Flag is disabled by default
  • Usually made public in beta 1 of version n-1
  • Users can enable with --enable-experiment=feature-name
2

Default enabled (Version n)

  • Feature flag is enabled by default in beta 1 of version n
  • Feature is still technically “experimental” but available to all
  • Monitor for issues and user feedback
3

Stabilization (Version n)

  • Feature runs in production for a full release cycle (~3 months)
  • Must not cause significant issues
  • Considered stable if feedback is positive
4

Retirement (Version n+1)

  • Flag is retired in beta 1 of version n+1
  • Feature is now permanent, flag has no effect
  • Cleanup work begins

Timeline Example

Here’s a concrete example for a hypothetical feature:
--enable-experiment=telepathy available
Feature is OFF by default
Users can opt-in for testing
Development timelines vary. Some features span multiple versions, while others complete within a single release cycle and only appear in dev releases.

When to Retire Flags

Retire an experimental flag when:
  1. ✅ Feature has been enabled by default for a full release cycle
  2. ✅ At least ~3 months have passed in stable
  3. ✅ No significant issues have been reported
  4. ✅ User feedback is positive
Don’t retire flags prematurely. Give features time to prove stability in production.

Flag Retirement Process

1

Clean up test usage

Remove or update tests that use the experimental flag:
# Find tests using the flag
grep -r "--enable-experiment=feature-name" tests/

# Update tests to remove flag usage
Tests should work without the flag since it’s enabled by default.
2

Clean up non-test code

Ensure SDK code doesn’t rely on checking the experimental flag:
# Search for flag references in source
grep -r "feature-name" sdk/ pkg/
Remove conditional logic based on the flag.
3

Verify package tests

Check that tests in /pkg/* don’t rely on the flag:
grep -r "enable-experiment" pkg/
4

Mark flag as expired

Edit tools/experimental_features.yaml:
feature-name:
  help: "Enable the telepathy feature"
  enabledIn: '6.2'
  expired: true  # Add this line
5

Update generated code

Run the update scripts to regenerate flag-handling code:
# Scripts that regenerate flag code
./tools/generate_experimental_flags.dart
6

Alert EngProd team

Notify the Engineering Productivity team that retirement is complete.They will coordinate:
  • Final verification
  • SDK version increment
  • Release notes updates

Common Flag Patterns

Checking Flag Status in Code

During development, you may need to check if a flag is enabled:
// Analyzer
if (analysisContext.analysisOptions.experimentStatus.feature-name) {
  // Feature-specific behavior
}

// CFE
if (experimentalFlags.featureName) {
  // Feature-specific behavior  
}
Remove these conditionals during flag retirement. The feature should always be active.

Test Annotations

Tests may require specific experiments:
// @dart=2.19
// @dart=3.0
// @Experiments=feature-name

main() {
  // Test code
}
Remove experiment annotations when retiring flags.

Finding Flag Usage

Useful commands for finding flag references:
# Find flag in tests
grep -r "enable-experiment=feature-name" tests/

# Find flag in analysis options
find . -name "analysis_options.yaml" -exec grep -l "feature-name" {} \;

# Find flag in source code
rg "feature-name" sdk/ pkg/ --type dart

# Find experiment annotations
grep -r "@Experiments" tests/

Exceptions and Special Cases

Short Development Cycles

Some features complete development quickly:
  • Development happens entirely in dev releases
  • Never available in beta or stable with flag
  • Enabled by default immediately upon completion
  • Can be retired in the next release

Extended Development

Large features may take multiple releases:
  • Null safety took several versions to complete
  • Flag may exist across many SDK versions
  • Multiple beta cycles for user feedback
  • Extended stabilization period

Deprioritized Features

If feature development is paused:
  • Keep flag disabled by default
  • Don’t retire until development resumes and completes
  • Document the status in the YAML file

Best Practices

Document thoroughly

Add clear comments in experimental_features.yaml about the feature’s purpose and status

Test both states

During development, test with flag both enabled and disabled

Clean as you go

Remove flag dependencies during retirement, don’t accumulate technical debt

Coordinate releases

Work with EngProd team to align flag retirement with release milestones

Resources

Build docs developers (and LLMs) love