Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Expand Feature flags #2591

Merged
merged 17 commits into from
Jun 5, 2020
Merged

Expand Feature flags #2591

merged 17 commits into from
Jun 5, 2020

Conversation

senadir
Copy link
Member

@senadir senadir commented May 29, 2020

closes #2451

This PR changes and expand our feature flags to make them more dynamic and to work with the core/plugin/dev model, so it does the following:

  • It changes our flags names from stable and experimental to numbers, this is a requirement so we can have collapsable flags, so we have 3 flags:

    • experimental is now 3.
    • plugin is now 2.
    • core is now 1.
      for most cases you always compare up, this means if you want something to work only in dev you check if the flag is bigger than 2 WOOCOMMERCE_BLOCKS_PHASE > 2, if you want something to be in the feature plugin you compare for bigger than 1 WOOCOMMERCE_BLOCKS_PHASE > 1.
  • It updates our build process slightly, core and stable files and blocks should live stableMainEntry and stableFrontEndEntry, this means things like Cart, Checkout, and other blocks, new unstable blocks like Single Product should live in experimentalMainEntry or experimentalFrontEndEntry, those files are only built if you run npm run build and npm start, they won't be built if you run npm run deploy, you can force not building them locally by passing a different flag so WOOCOMMERCE_BLOCKS_PHASE=2 npm run build.

  • There's now a constant named WC_BLOCKS_IS_FEATURE_PLUGIN, it should be defined if we're in running feature plugin and won't be defined when running from core, in most cases, you won't use this constant.

  • in PHP you will still use WOOCOMMERCE_BLOCKS_PHASE to define your features, same as PHP with same exact flags.

  • In JS, you won’t be using process.env.WOOCOMMERCE_BLOCKS_PHASE anymore, you will have to use WOOCOMMERCE_BLOCKS_PHASE a constant exported from blocks settings

import { WOOCOMMERCE_BLOCKS_PHASE } from '@woocommerce/block-settings';

if ( WOOCOMMERCE_BLOCKS_PHASE > 1 ) {
	registerBlockType( 'woocommerce/checkout', settings );
}
if ( WOOCOMMERCE_BLOCKS_PHASE > 2 ) {
	registerBlockType( 'woocommerce/single-product', settings );
}
  • woocommerce/feature-flag eslint rule is disabled until we merge this and update it.

  • Our deployment command npm run deploy will now append WOOCOMMERCE_BLOCKS_PHASE=2 this means when you deploy, it will work on the context of feature plugin, but WooCommrce core will consume it on the context of Core automatically.

How should you gate your code?

In most cases, you will have code that is:

  • safe to run in WooCommerce core, do nothing.
  • safe to run in the feature plugin only, it will be either:
    • A file, wrap the enqueue of that file in a flag.
    • A code block, wrap it in WOOCOMMERCE_BLOCKS_PHASE.
  • A code that only runs in development or experimental, it will be either:
    • A file, wrap the enqueue, this is the most important part.
    • Skip building the file (to reduce the final zip file) by moving it to experimentalMainEntry or experimentalFrontEndEntry.
    • A code block, wrap it in WOOCOMMERCE_BLOCKS_PHASE.
    • A JS code block, if it’s heavy and will affect the final bundle size, we can see into reintroducing dead code elimination (I didn't add this because I wanted to reduce complexity unless we have a use case for it).

Test instructions:

  • Build the plugin normally npm run build, you should be able to add any block, including Cart and Checkout and Single Product.

  • Build the plugin in feature plugin mode WOOCOMMERCE_BLOCKS_PHASE=2 npm run build, you should be able to add any block, including Cart and Checkout but not Single Product.

  • Build plugin in Core mode WOOCOMMERCE_BLOCKS_PHASE=1 npm run build, you should be able to add our core blocks, and not Cart, Checkout, or Single Product.

  • If you want to further test, you can try to create a zip WOOCOMMERCE_BLOCKS_PHASE=2 npm run package-plugin to generate a zip that you can upload to other websites, it would simulate a feature plugin.

src/Package.php Outdated Show resolved Hide resolved
@senadir senadir force-pushed the add/more-feature-flags branch from afe1a4a to 00d5fff Compare May 29, 2020 11:51
@github-actions
Copy link
Contributor

update this rule to match the new flags.

update this rule to match the new flags.


https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/00d5fff13f57fb6956ff7bcc50a1c64193f9fd27/bin/eslint-plugin-woocommerce/rules/feature-flag.js#L38-L41

🚀 This comment was generated by the automations bot based on a todo comment in 00d5fff in #2591. cc @senadir

@github-actions
Copy link
Contributor

github-actions bot commented May 29, 2020

Size Change: +1.75 kB (0%)

Total Size: 1.75 MB

Filename Size Change
build/active-filters-frontend.js 7.2 kB +36 B (0%)
build/active-filters.js 7.94 kB +56 B (0%)
build/all-products-frontend.js 72.3 kB +25 B (0%)
build/all-products.js 20.6 kB +73 B (0%)
build/all-reviews-legacy.js 9.21 kB +21 B (0%)
build/all-reviews.js 9.51 kB +56 B (0%)
build/attribute-filter-frontend.js 16.6 kB +24 B (0%)
build/attribute-filter.js 11.5 kB +57 B (0%)
build/blocks-legacy.js 2.92 kB -2 B (0%)
build/blocks.js 2.92 kB -2 B (0%)
build/cart-frontend.js 97.3 kB +17 B (0%)
build/cart.js 32.6 kB +74 B (0%)
build/checkout-frontend.js 114 kB +23 B (0%)
build/checkout.js 38 kB +64 B (0%)
build/featured-category-legacy.js 7.27 kB +11 B (0%)
build/featured-category.js 7.59 kB +57 B (0%)
build/featured-product-legacy.js 9.52 kB +17 B (0%)
build/featured-product.js 9.84 kB +63 B (0%)
build/handpicked-products-legacy.js 6.92 kB +14 B (0%)
build/handpicked-products.js 7.24 kB +60 B (0%)
build/price-filter-frontend.js 14.1 kB +36 B (0%)
build/price-filter.js 10 kB +64 B (0%)
build/product-best-sellers-legacy.js 7 kB +16 B (0%)
build/product-best-sellers.js 7.31 kB +54 B (0%)
build/product-categories-legacy.js 3.22 kB +4 B (0%)
build/product-categories.js 3.21 kB -1 B
build/product-category-legacy.js 7.91 kB +13 B (0%)
build/product-category.js 8.22 kB +61 B (0%)
build/product-new-legacy.js 7.15 kB +15 B (0%)
build/product-new.js 7.47 kB +59 B (0%)
build/product-on-sale-legacy.js 7.52 kB +16 B (0%)
build/product-on-sale.js 7.88 kB +63 B (0%)
build/product-search-legacy.js 3.14 kB +15 B (0%)
build/product-search.js 3.43 kB +61 B (1%)
build/product-tag-legacy.js 6.09 kB +12 B (0%)
build/product-tag.js 6.4 kB +71 B (1%)
build/product-top-rated-legacy.js 7.12 kB +16 B (0%)
build/product-top-rated.js 7.45 kB +60 B (0%)
build/products-by-attribute-legacy.js 7.88 kB +13 B (0%)
build/products-by-attribute.js 8.2 kB +59 B (0%)
build/reviews-by-category-legacy.js 11.2 kB +18 B (0%)
build/reviews-by-category.js 11.5 kB +60 B (0%)
build/reviews-by-product-legacy.js 12.7 kB +16 B (0%)
build/reviews-by-product.js 13 kB +59 B (0%)
build/reviews-frontend-legacy.js 8.05 kB +23 B (0%)
build/reviews-frontend.js 8.91 kB +27 B (0%)
build/single-product-frontend.js 75.2 kB +24 B (0%)
build/single-product.js 14.8 kB +68 B (0%)
build/vendors-legacy.js 376 kB -1 B
build/vendors.js 457 kB +7 B (0%)
ℹ️ View Unchanged
Filename Size Change
build/block-error-boundary-legacy.js 775 B 0 B
build/block-error-boundary.js 774 B 0 B
build/custom-select-control-style-legacy.js 782 B 0 B
build/custom-select-control-style.js 783 B 0 B
build/editor-legacy-rtl.css 12.5 kB 0 B
build/editor-legacy.css 12.5 kB 0 B
build/editor-rtl.css 13.5 kB 0 B
build/editor.css 13.5 kB 0 B
build/product-list-style-legacy.js 775 B 0 B
build/snackbar-notice-style-legacy.js 778 B 0 B
build/snackbar-notice-style.js 779 B 0 B
build/spinner-style-legacy.js 772 B 0 B
build/spinner-style.js 772 B 0 B
build/style-legacy-rtl.css 5.5 kB 0 B
build/style-legacy.css 5.5 kB 0 B
build/style-rtl.css 17 kB 0 B
build/style.css 17 kB 0 B
build/vendors-style-legacy-rtl.css 1.03 kB 0 B
build/vendors-style-legacy.css 1.03 kB 0 B
build/vendors-style-legacy.js 102 B 0 B
build/vendors-style-rtl.css 1.03 kB 0 B
build/vendors-style.css 1.03 kB 0 B
build/vendors-style.js 102 B 0 B
build/wc-blocks-data.js 7.08 kB 0 B
build/wc-blocks-middleware.js 931 B 0 B
build/wc-blocks-registry.js 1.79 kB 0 B
build/wc-payment-method-cheque.js 794 B 0 B
build/wc-payment-method-paypal.js 830 B 0 B
build/wc-payment-method-stripe.js 11.9 kB 0 B
build/wc-settings.js 2.14 kB 0 B
build/wc-shared-context.js 1.51 kB 0 B

compressed-size-action

@senadir senadir changed the title Add/more feature flags Expand Feature flags May 29, 2020
@senadir senadir self-assigned this May 29, 2020
@senadir senadir added the tools Used for work on build or release tools. label May 29, 2020
@senadir senadir marked this pull request as ready for review May 29, 2020 13:13
@senadir senadir requested a review from a team as a code owner May 29, 2020 13:13
@senadir senadir requested review from mikejolley and nerrad and removed request for a team May 29, 2020 13:13
@senadir senadir added the skip-changelog PRs that you don't want to appear in the changelog. label May 29, 2020
Copy link
Member

@mikejolley mikejolley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some feedback/qu/comments.

@@ -51,4 +51,6 @@ const settings = {
},
};

registerBlockType( 'woocommerce/cart', settings );
if ( WOOCOMMERCE_BLOCKS_PHASE > 1 ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it would be useful to add a util function for this that supports named properties? e.g. is_build( 'experimental' )

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah now that we don’t have dead code elimination this could be useful, and it could help enforce tighter guidelines like throwing an error if the flag you provided doesn’t exist.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we're going to add convenience utility functions, it'd be nicer to do something like isExperimentalBuild() or isStableBuild().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where should those methods live 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where should those methods live

Good question! Maybe block settings (since that's where WOOCOMMERCE_BLOCKS_PHASE comes from)?

@@ -51,4 +51,6 @@ const settings = {
},
};

registerBlockType( 'woocommerce/cart', settings );
if ( WOOCOMMERCE_BLOCKS_PHASE > 1 ) {
registerBlockType( 'woocommerce/cart', settings );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could instead add a wrapper around registerBlockType that supports a phase argument. It might make registration easier and improve DRY, and remove the need to import the settings everywhere.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this! Maybe registerGatedBlockType?

// eslint-disable-next-line woocommerce/feature-flag
process.env.WOOCOMMERCE_BLOCKS_PHASE || 'experimental'
),
} ),
],
resolve,
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to get someone else to review webpack who knows more :)

src/Assets.php Outdated

if ( 'experimental' === WOOCOMMERCE_BLOCKS_PHASE ) {
if ( WOOCOMMERCE_BLOCKS_PHASE > 2 ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again a corresponding PHP utility function might be useful. It's also clearer to read code if the phases are named - but you don't need to remove the numbered phases from behind the scenes, they are fine.

if ( WOOCOMMERCE_BLOCKS_PHASE > 1 ) {
self::register_script( 'wc-checkout-block', plugins_url( self::get_block_asset_build_path( 'checkout' ), __DIR__ ), $block_dependencies );
self::register_script( 'wc-cart-block', plugins_url( self::get_block_asset_build_path( 'cart' ), __DIR__ ), $block_dependencies );
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point we should move script registration to the block classes - I always forget to add them here :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, there's a bunch of things I'd like to do around that eventually (including incorporating using block.json files which can contain all the things around the blocks and thus make the initialization code more dynamic).

}
define( 'WOOCOMMERCE_BLOCKS_PHASE', $flag );

$allowed_flags = [ '1', '2', '3' ];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we use strings here instead of numbers?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Environment variables and ini values are treated as strings, to avoid casting and recasting things all over the place, I assume everything is a string and when I define the flag I turn it to an int.

src/Domain/Bootstrap.php Outdated Show resolved Hide resolved
@@ -20,6 +20,7 @@

$minimum_wp_version = '5.2';

define( 'WC_BLOCKS_IS_FEATURE_PLUGIN', true );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe check if this is defined first just in case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! That would also allow for overriding in wp-config.php if necessary.

Copy link
Contributor

@nerrad nerrad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tackling this Nadir! It's come along really great.

I've left a few comments and also agree with the feedback Mike has given as well. Almost ready to ship!

@@ -51,4 +51,6 @@ const settings = {
},
};

registerBlockType( 'woocommerce/cart', settings );
if ( WOOCOMMERCE_BLOCKS_PHASE > 1 ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we're going to add convenience utility functions, it'd be nicer to do something like isExperimentalBuild() or isStableBuild().

@@ -51,4 +51,6 @@ const settings = {
},
};

registerBlockType( 'woocommerce/cart', settings );
if ( WOOCOMMERCE_BLOCKS_PHASE > 1 ) {
registerBlockType( 'woocommerce/cart', settings );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this! Maybe registerGatedBlockType?

};

const mainEntry =
process.env.WOOCOMMERCE_BLOCKS_PHASE === 'stable'
// env variables are strings, so we compare against a string, so we need to parse it.
parseInt( process.env.WOOCOMMERCE_BLOCKS_PHASE, 10 ) < 3
? stableMainEntry
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this means that we still have dead code removal on releases for anything marked experimental right? Might be a good idea to also check that process.env.WOOCOMMERCE_BLOCKS_PHASE is defined before using here? If it's undefined won't this cause a ReferenceError (especially since it looks like we no longer define a default for process.env.WOOCOMMERCE_BLOCKS_PHASE)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn’t cause a Reference Error because process.env is always defined, WOOCOMMERCE_BLOCKS_PHASE not being defined will result in experimental files being generated which is safer than them not being generated right?

Copy link
Contributor

@nerrad nerrad Jun 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am okay with experimental files being built if WOOCOMMERCE_BLOCKS_PHASE is not defined, that's not what I'm calling out here. I thought we might have an issue if parseInt returned NaN but it looks like we won't:

Image 2020-06-02 at 3 32 39 PM

So all is okay here (assuming node behaves the same).

if ( WOOCOMMERCE_BLOCKS_PHASE > 1 ) {
self::register_script( 'wc-checkout-block', plugins_url( self::get_block_asset_build_path( 'checkout' ), __DIR__ ), $block_dependencies );
self::register_script( 'wc-cart-block', plugins_url( self::get_block_asset_build_path( 'cart' ), __DIR__ ), $block_dependencies );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, there's a bunch of things I'd like to do around that eventually (including incorporating using block.json files which can contain all the things around the blocks and thus make the initialization code more dynamic).

@@ -20,6 +20,7 @@

$minimum_wp_version = '5.2';

define( 'WC_BLOCKS_IS_FEATURE_PLUGIN', true );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! That would also allow for overriding in wp-config.php if necessary.

@senadir senadir force-pushed the add/more-feature-flags branch from 5f19afa to a5b647d Compare June 3, 2020 16:22
@senadir
Copy link
Member Author

senadir commented Jun 3, 2020

This is ready for another review now.

@senadir
Copy link
Member Author

senadir commented Jun 3, 2020

The last commits adds:

  • a check for WC_BLOCKS_IS_FEATURE_PLUGIN before defining it.
  • some JS helpers like
registerExperimentalBlockType( name, settings ) // returns the block or undefined
registerFeaturePluginBlockType( name, settings ) // returns the block or undefined
isExperimentalBuild() // returns Boolean
isFeaturePluginBuild() // returns Boolean

it also adds helpers in PHP

Package::is_experimental_build(); // returns Boolean
Package::is_feature_plugin_build(); // returns Boolean

Copy link
Contributor

@nerrad nerrad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking really good Nadir 👏 ! Thanks for handling all the feedback and steadily improving this pull.

I've got a few comments and the only one I'm really concerned about the most is the new methods in src/Package.php. Once we get that comment addressed this should be ready to go.

@@ -0,0 +1,57 @@
/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 on putting these in their own file (and the name is great)!

/**
* External dependencies
*/
import { registerBlockType } from '@wordpress/blocks';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One concern I have here is that we are now loading this api on the frontend for anything using block-settings right? is that problematic? I think I might have been the one who suggested putting this api in block-settings so that could be my fault here.

It doesn't look like it's affected build sizes significantly, so maybe this would be okay (since @woocommerce/block-settings is essentially just an alias for the path and thus it should be tree-shakeable).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had similar conerns, but I'm assuming this is tree shakeable, bundle bot didn’t report any extra weight so It’s safe.

*
* @return {?Object} The block, if it has been successfully registered;
* otherwise `undefined`.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for being so thorough with the comment blocks here 👏 !

src/Package.php Outdated Show resolved Hide resolved
@nerrad nerrad added this to the 2.7.0 milestone Jun 5, 2020
@nerrad nerrad added the status: blocker Used on issues or pulls that block work from being released. label Jun 5, 2020
Copy link
Contributor

@nerrad nerrad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Let's :shipit:

@senadir senadir force-pushed the add/more-feature-flags branch from 6be775f to 43572e7 Compare June 5, 2020 18:03
@senadir senadir merged commit 9b76ea7 into master Jun 5, 2020
@senadir senadir deleted the add/more-feature-flags branch June 5, 2020 19:13
@Aljullu Aljullu mentioned this pull request Jun 8, 2020
11 tasks
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
skip-changelog PRs that you don't want to appear in the changelog. status: blocker Used on issues or pulls that block work from being released. tools Used for work on build or release tools.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Tweak Experimental Flag feature gating to allow for holding back from WC core.
4 participants