Skip to content

build-time feature flags aka sveltable features, deprecations, etc - #1247

Open
NullVoxPopuli wants to merge 4 commits into
mainfrom
nvp/build-time-feature-flags
Open

NullVoxPopuli wants to merge 4 commits into
mainfrom
nvp/build-time-feature-flags

Conversation

@NullVoxPopuli

Copy link
Copy Markdown
Contributor

Summary

This pull request is proposing a new RFC.

To succeed, it will need to pass into the Exploring Stage, followed by the Accepted Stage.

A Proposed or Exploring RFC may also move to the Closed Stage if it is withdrawn by the author or if it is rejected by the Ember team. This requires an "FCP to Close" period.

An FCP is required before merging this PR to advance to Accepted.

Upon merging this PR, automation will open a draft PR for this RFC to move to the Ready for Released Stage.

Exploring Stage Description

This stage is entered when the Ember team believes the concept described in the RFC should be pursued, but the RFC may still need some more work, discussion, answers to open questions, and/or a champion before it can move to the next stage.

An RFC is moved into Exploring with consensus of the relevant teams. The relevant team expects to spend time helping to refine the proposal. The RFC remains a PR and will have an Exploring label applied.

An Exploring RFC that is successfully completed can move to Accepted with an FCP is required as in the existing process. It may also be moved to Closed with an FCP.

Accepted Stage Description

To move into the "accepted stage" the RFC must have complete prose and have successfully passed through an "FCP to Accept" period in which the community has weighed in and consensus has been achieved on the direction. The relevant teams believe that the proposal is well-specified and ready for implementation. The RFC has a champion within one of the relevant teams.

If there are unanswered questions, we have outlined them and expect that they will be answered before Ready for Release.

When the RFC is accepted, the PR will be merged, and automation will open a new PR to move the RFC to the Ready for Release stage. That PR should be used to track implementation progress and gain consensus to move to the next stage.

Checklist to move to Exploring

  • The team believes the concepts described in the RFC should be pursued.
  • The label S-Proposed is removed from the PR and the label S-Exploring is added.
  • The Ember team is willing to work on the proposal to get it to Accepted

Checklist to move to Accepted

  • This PR has had the Final Comment Period label has been added to start the FCP
  • The RFC is announced in #news-and-announcements in the Ember Discord.
  • The RFC has complete prose, is well-specified and ready for implementation.
    • All sections of the RFC are filled out.
    • Any unanswered questions are outlined and expected to be answered before Ready for Release.
    • "How we teach this?" is sufficiently filled out.
  • The RFC has a champion within one of the relevant teams.
  • The RFC has consensus after the FCP period.

@github-actions github-actions Bot added the S-Proposed In the Proposed Stage label Oct 1, 2026
@NullVoxPopuli
NullVoxPopuli marked this pull request as ready for review October 1, 2026 00:55
@NullVoxPopuli
NullVoxPopuli marked this pull request as ready for review October 1, 2026 00:55
@ef4

ef4 commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

I like the general goal but I think this RFC is talking about multiple different things without explaining them. These are separate concepts (some of which are layered on each other):

  • optional features
  • EmberEnv
  • EmberEnv.FEATURES
  • config/environment.js
  • app/config/environment.js
  • @embroider/config-meta-loader
  • canary features
  • babel-plugin-debug-macros (like import DEBUG from '@glimmer/env')

The RFC seems to want to try to leave all that mess untouched and just change the mechanism underneath, but that just adds to learning burden.

I think we can design a clear and coherent small new core and simultaneously deprecate all the old things with deprecation guides that show how you can do each case with the small new core.

We don't need to retcon how existing flags work. They can continue working exactly the same until 8.0 or whenever the deprecation period ends. And that doesn't need to slow down adoption -- because a new flag in the new small core can strip the old behavior away immediately. import.meta.env?.EMBER_DROP_LEGACY_CONFIG_ENV

For the new small core, I think we go even simpler than what you've described. Something like:

  • Ember's public API for deciding whether to use non-default behaviors is by reading import.meta.env?.EMBER_SOME_FEATURE.
  • All feature flags are designed so that the default value is falsy, so that if you do nothing special you get the correct default.
  • We promise that every place in Ember that reads the flags literally uses the exact expression import.meta.env?.EMBER_SOME_FEATURE. They're never re-exported, re-bound to new names, etc. This puts the smallest burden on future build environment's ability to optimize away dead code. And it's not a big deal for us -- it's quite clear and readable in the ember source. It's clearer than needing to depend on a special env package.
  • Part of what makes this simple is agreeing to always make the default value be false. When we reach a major and decide to make an optional behavior into a default behavior, that flag goes away. If we decide to still keep the old behavior around as now-non-default behavior, that's a new flag because it's a new non-default behavior.

I think it's reasonable to hash out that small core and deprecate the old things in a single RFC, because the two are so entwined. We don't want to ship a new thing without being sure it actually replaces all the old things, and we don't want to deprecate the old things without having their clear replacement.

The things to deprecate probably include:

  • the whole config/environment.js system (the one that executes a file in node, serializes it, and then makes it available as a similarly named runtime module). The replacement here is to have a normal JS module at app/config/environment.js. This file is the correct place to put runtime config. If it needs to incorporate some build time flags of its own, people should do that via their own mport.meta.env.
    • I realize config-in-meta has been our default behavior for a long time, but the vast majority of apps don't actually care. The ones that do care necessarily already have custom code for emitting a dynamic meta tag on the server, so generating the tag is not really in-scope for us to do automatically for you. Those cases can easily put their own data loading code inside app/config/environment.js to load only the parts of the config that actually want to come from the server.
  • @ember/optional-features. The replacement is that we can offer new meta.env versions of existing optional features (although I don't think we currently have any optional features, so that makes the removal even easier -- we just decide to never add a new one)
  • window.EmberEnv. This is replaced by import.meta.env. I would add that this doesn't necessarily take away the ability to manipulate flags at runtime. That becomes a choice in your build tooling. You can choose to leave some or all import.meta.env expressions untranspiled, and then manipulate the values at runtime. This works out of the box by default in vite in development mode -- import.meta.env is a real runtime object and it's mutable.
  • import { DEBUG} from '@glimmer/env' this needs to get deprecated for sure. Internally we already stopped needing it in apps, but other code in the ecosystem has always been able to rely on it working, so we keep the babel support in place today. The replacement for addons can be documented as export conditions (like ember-source does). For apps it's probably "follow your build tool's standard advice", meaning for vite import.meta.env.{DEV,PROD}.
  • canary features: we don't have any of these right now, but docs remain. I think for this we would still be able to use import.meta.env flags while developing the feature. We can always compile them away before shipping if we really want to (although I would prefer to just give them a special namespace like import.meta.env.UNSTABLE_EMBER_* and keep them in). This RFC could propose that special namespace and make it clear we reserve the right to break any code behind an unstable flag.

@kategengler

Copy link
Copy Markdown
Member

Discussing what the new APIs replace in the RFC is a good thing to do. I still think the deprecation RFC should follow the replacement RFC. When we combine both the new feature and the deprecations that follow on into one RFC we lose a lot of fidelity in tracking the deprecations and the RFC.

As part of making a new feature 'Recommended' we open the deprecation RFCs that stem from it and ideally at that point the community is happy with the new feature and ready for the old ways to go away.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-Proposed In the Proposed Stage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants