Evolving Front-End Best Practices, WordPress Realities, and a Case Study on Script Versioning Failures

For much of my career as a WordPress developer, I approached front-end performance optimization with a familiar set of “best practices”: minimize everything, combine everything, strip everything, and let the browser load as little as possible. If a setting promised leaner scripts or fewer bytes, I was inclined to enable it.

But the landscape of frontend performance has changed dramatically. HTTP/2, HTTP/3, modern JavaScript bundlers, module loaders, caching layers, edge networks, and auto-updating plugins have fundamentally altered what “optimal” really means.

Recently, I ran into a real-world example that illustrated this perfectly—a failure so subtle and inconsistent that I couldn’t reproduce it on my machine. Users were reporting that a Gravity Forms form simply didn’t load. No errors on my end, no obvious broken scripts. But the deeper I dug, the clearer it became: some “classic” optimization techniques are now actively harmful.

This post is meant to document what happened, why it happened, and what it means for modern performance strategy—both for myself and for other developers who may run into similar issues.

The Problem: A Form That Loaded for Some Users, Broke for Others

The scenario was straightforward:

  • A contact form (Gravity Forms) failed to load for certain users
  • I couldn’t reproduce it using the same OS and browser
  • No PHP errors in debug mode
  • No Gravity Forms log errors
  • But affected users saw this JavaScript error in DevTools:
Uncaught TypeError: Cannot read properties of undefined (reading 'call')

This error almost always indicates a module loader trying to call a function on a dependency that never loaded.

The key insight: a module was missing or mismatched.

And then we found the smoking gun.

The Culprit: “Remove Query Strings from Static Resources”

The SiteGround Speed Optimizer plugin has a setting called “Remove Query Strings From Static Resources.”

This used to be recommended in the early 2010s because older HTTP/1.1 proxies wouldn’t cache files with query strings. But in 2025?

It breaks things. Often catastrophically.

Why Removing Query Strings Breaks Gravity Forms

1. Gravity Forms uses version numbers for cache invalidation

Removing version strings prevents browsers from knowing when the script updates. This leads to some users loading old cached scripts while others load new ones — causing mismatched module versions and runtime failures.

2. Auto-updates create mismatched dependency versions

When Gravity Forms updates, the asset names stay the same (because the query strings were stripped), so browsers believe nothing has changed.

3. Modern protocols make this optimization unnecessary

HTTP/2 and HTTP/3 make parallel loading trivial. CDNs have no issues caching query-string assets.

The Bigger Lesson: Old Optimization Advice Can Be Actively Harmful

Many traditional “best practices” are outdated:

  • Combining scripts
  • Stripping query strings
  • Over-aggressive minification
  • Defer/async everything without context
  • Lazy loading everything

These techniques increasingly break modern dependency graphs, script loaders, caching layers, and plugin pipelines.

Case Study Summary

❌ Old belief:

“Removing query strings improves performance.”

✔ Modern truth:

“It breaks cache busting and can destabilize JavaScript.”

❌ Assumption:

“If I can’t reproduce the bug, it’s a user issue.”

✔ Reality:

Chrome was serving different cached versions to different users.

The Fix

  1. Disabled “Remove Query Strings”
  2. Whitelisted Gravity Forms assets from optimization
  3. Implemented long-term exclusions to prevent recurrence

The Takeaway for Developers

Modern performance optimization is about:

  • Respecting dependency graphs
  • Proper caching and invalidation
  • Leaning on HTTP/2+
  • Avoiding destructive legacy optimizations

Some “best practices” from 10 years ago now cause more harm than good.

BONUS: let’s prevent Gravity Forms from breaking again if the setting gets accidentally turned on:

Throw this into a custom plugin, or your theme’s function.php

/**
 * Exclude all Gravity Forms scripts from SiteGround Speed Optimizer JS optimization.
 */
add_filter( 'sg_optimizer_js_minify_exclude', function( $exclude_list ) {

	$gf_handles = array(
		'gform_gravityforms',      // main GF script.
		'gform_conditional_logic', // conditional logic.
		'gform_json',              // GF JSON/form init.
		'gform_field_filters',     // filter logic.
		'gform_masked_input',      // masked input fields.
		'gform_datepicker',        // GF datepicker.
		'gform_ui',                // UI helpers.
		'gform_form_js',           // GF form loader.
	);

	return array_merge( $exclude_list, $gf_handles );
});

/**
 * Exclude Gravity Forms styles from SiteGround Speed Optimizer CSS optimization.
 */
add_filter( 'sg_optimizer_css_minify_exclude', function( $exclude_list ) {

	$gf_styles = array(
		'gforms_reset_css',
		'gforms_formsmain_css',
		'gforms_ready_class_css',
		'gforms_browse_button_css',
	);

	return array_merge( $exclude_list, $gf_styles );
});

/**
 * Prevent SG Optimizer from stripping query strings from Gravity Forms scripts/styles.
 */
add_filter( 'sg_optimizer_remove_query_strings_exclude', function( $exclude_list ) {

	$patterns = array(
		'/gravityforms/js/',  // JS files.
		'/gravityforms/css/', // CSS files.
		'gform',              // fallback catch-all.
	);

	return array_merge( $exclude_list, $patterns );
});

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.