
Releasing with WordPress 6.3 is a new developer feature called Development Mode. It’s a new PHP constant with a few helper functions that give context to the type of development you are doing.
This adds to the myriad of ways plugin developers can use WordPress for debugging purposes.
Within this tutorial, I’ll cover the existing ways developers can debug WordPress and why Development Mode is a welcome addition to the WordPress codebase.
The WP_DEBUG constant and its uses

As a plugin developer, it’s pretty much a defacto requirement that you should develop your plugins (or at least test them) with WP_DEBUG
enabled. There might be a PHP deprecation notice or a _doing_it_wrong note that a core function is being accessed incorrectly. These errors don’t usually reveal themselves on their own unless you’ve been hit with a fatal (critical) error, and by then, it could be too late. It’s important to stay on top of deprecations and notices, as they will often point to areas in the code that need to be improved in the short and long term.
Turning on WP_DEBUG
is as simple as declaring a PHP constant.
The WP_DEBUG constant should be declared in the wp-config.php
file. If enabled, it’ll look like this:
define( 'WP_DEBUG', true );
Code language: PHP (php)
As a plugin developer, WP_DEBUG
is a useful constant for determining if the user would like extra information in regard to an error. You can simply check for the constant and take action if WP_DEBUG
mode is enabled. Here’s an example snippet that checks for WP_DEBUG
.
<?php
// $post variable defined above (assume variable is passed via function parameter).
$post = '';
// Check if $post is a WP_Post object. If not, throw error.
if ( ! is_a( $post, 'WP_Post' ) ) {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
// $post is not a WP_Post object, so don't use it.
_doing_it_wrong( 'DLXPlugins/WP_DEBUG', 'A non-post object was used for the post parameter.', '1.0.0' );
}
}
Code language: PHP (php)
Avoid altering plugin behavior for WP_DEBUG
Unfortunately, you can’t safely assume that those with WP_DEBUG enabled aren’t on production. In other words, enabling a beta feature when WP_DEBUG is enabled might not be the greatest idea. WP_DEBUG is best used for notices and warnings.
With WP_DEBUG
enabled, you might run into issues with headers being sent early or REST/Ajax calls not completing correctly due to unforeseen output. To prevent this output from interfering with program execution, it’s recommended to log these notices to file instead.
By defining constant WP_DEBUG_LOG
, you can log the errors to a file. You can do this by setting WP_DEBUG_LOG
to true
in the wp-config.php
file.
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
Code language: JavaScript (javascript)
By setting WP_DEBUG_LOG
to true
, the errors will be stored in the wp-content
folder under debug.log
.
.
└── wordpress-install/
├── wp-config.php
├── wp-admin
├── wp-includes
└── wp-content/
└── debug.log
Code language: AsciiDoc (asciidoc)
WP_DEBUG_LOG in-depth
Delicious Brains strikes again with a lovely overview of all of the possibilities of WP_DEBUG_LOG and its variations.
Since debug logs can contain sensitive information, you may not want these stored in a public location. You can modify WP_DEBUG_LOG
to point to a file not in the public path.
For example, this attempts to store a file named debug.log
one directory beneath the root:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', dirname( __FILE__, 2 ) . '/tmp/debug.log' );
define( 'WP_DEBUG_DISPLAY', false );
Code language: PHP (php)
Fortunately, you can disable error printing to the screen rather easily using the WP_DEBUG_DISPLAY
constant in the wp-config.php
file:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
Code language: PHP (php)
If you’re still getting notices to screen, you may need to tweak your php.ini
file and set the error_reporting
or use ini_set
.
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
error_reporting(0);
@ini_set('display_errors', 0);
Code language: JavaScript (javascript)
If you’re okay with having debug.log
in your wp-content
folder and are using a public domain, I recommend the following .htaccess
rule to prevent public viewing of the file.
<Files debug.log>
Order deny,allow
Deny from all
</Files>
Code language: Apache (apache)
For NGINX, you’d place this in your nginx.conf
file:
location ~ /debug.log {
deny all;
}
SCRIPT_DEBUG and its uses
Very much worth mentioning is SCRIPT_DEBUG
. This is such a useful constant. When you set it to true, WordPress will serve unminified versions of most of its scripts and styles. From what I’ve seen, for the most part, a lot of plugin and theme authors use it, too, particularly when it comes to version numbers when registering and cache-busting assets.
You’d place SCRIPT_DEBUG
in the wp-config.php
file:
define( 'SCRIPT_DEBUG', true );
Code language: PHP (php)
Here’s an example of serving an unminified version of a JavaScript file when SCRIPT_DEBUG
is true
.
<?php
add_action( 'wp_enqueue_scripts', 'dlx_enqueue_scripts' );
/**
* Enqueue scripts.
*/
function dlx_enqueue_scripts() {
// Serve unminified if SCRIPT_DEBUG is true.
$suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
$script_file = 'dlx-example' . $suffix . '.js';
$script_path = plugin_dir_url( __FILE__ ) . 'js/' . $script_file;
wp_enqueue_script(
'dlx-example',
$script_path,
array(),
'1.0.0',
true
);
}
Code language: PHP (php)
This, of course, assumes a directory structure as follows:
.
└── dlx-example/
└── js/
├── dlx-example.js
└── dlx-example.min.js
Code language: AsciiDoc (asciidoc)
Using SCRIPT_DEBUG
is a lifesaver when debugging styles and JavaScript errors. It is a widely used constant and most reputable plugin and theme authors honor it. Chances are, if you have WP_DEBUG
enabled, you’ll also want SCRIPT_DEBUG
set as well.
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', dirname( __FILE__ ) . '/wp-content/mydebug.log' );
define( 'WP_DEBUG_DISPLAY', false );
define( 'SCRIPT_DEBUG', true );
Code language: PHP (php)
Plugin break: Query Monitor
If you’re doing any local WordPress development, you need Query Monitor. It’s totally free, and it makes debugging and troubleshooting WordPress or PHP-related errors much easier.
For more debugging plugins, please check out these 18 free debugging plugins, written by Jeff Starr of Digging into WordPress.
WP_LOCAL_DEV and its uses
By defining WP_LOCAL_DEV
to true
in the wp-config.php
file, you can indicate that you are using WordPress locally.
WP_LOCAL_DEV isn't widely used and is not recommended
Plugins such as Jetpack use WP_LOCAL_DEV, but not much else. Use WP_ENVIRONMENT_TYPE instead.
WP_LOCAL_DEV is a more controversial constant and isn’t even officially documented. There’s also a Trac ticket to remove it from site health. I felt I would mention it here for the completeness of this tutorial since it technically is in use.
WP_ENVIRONMENT_TYPE and its uses
When you’re developing sites, chances are you’re working on a local or development environment. When showing sites to clients, you’re typically on a production or staging environment.
Fortunately, with WordPress, you can tell which environment a user is using by making use of the WP_ENVIRONMENT_TYPE
PHP constant.
WP_ENVIRONMENT_TYPE
can be four values:
- local
- development
- staging
- production
It would be defined in wp-config.php
:
// wp-config.php file.
define( 'WP_ENVIRONMENT_TYPE', 'local' );
Code language: PHP (php)
Plugin authors can use function wp_get_environment_type to get the environment type.
<?php
/**
* Load library based on environment type.
*
* @package DLXPlugins
*/
switch ( wp_get_environment_type() ) {
case 'local':
case 'development':
// Load experimental library.
break;
case 'staging':
case 'production':
// Load stable library.
break;
}
Code language: PHP (php)
Here’s another example that is designed as an mu-plugin. This checks to see if a plugin is active and includes a file directly if on production. I’ve done this in the past when we needed to develop a WordPress plugin, but on production, we include it as an mu-plugin
.
<?php
/**
* Check if the plugin is active.
*
* @param string $path Path to the plugin file (e.g., akisimet/akismet.php).
*
* @return bool True if the plugin is active, false otherwise.
*/
function mu_dlx_block_assist_is_activated( $path ) {
// Gets all active plugins on the current site.
$active_plugins = get_option( 'active_plugins', array() );
if ( in_array( $path, $active_plugins, true ) ) {
return true;
}
return false;
}
// Check if core plugin is active, if not, load mu-plugin.
if ( ! mu_dlx_block_assist_is_activated( 'my-plugin/my-plugin.php' ) ) {
if ( 'production' === wp_get_environment_type() ) {
// Load the mu-plugin if it exists.
if ( file_exists( WP_CONTENT_DIR . '/mu-plugins/my-mu-plugin/my-plugin.php' ) ) {
require_once WP_CONTENT_DIR . '/mu-plugins/my-mu-plugin/my-plugin.php';
}
}
}
Code language: PHP (php)
WP_ENVIRONMENT_TYPE and WP_DEBUG
WP_ENVIRONMENT_TYPE
and WP_DEBUG
can go hand-in-hand. For example, if you set WP_ENVIRONMENT_TYPE
to development
, WP_DEBUG will be set to true for you. Here’s a sample of the core code in the linked example:
// Add define( 'WP_DEBUG', true ); to wp-config.php to enable display of notices during development.
if ( ! defined( 'WP_DEBUG' ) ) {
if ( 'development' === wp_get_environment_type() ) {
define( 'WP_DEBUG', true );
} else {
define( 'WP_DEBUG', false );
}
}
Code language: PHP (php)
As you can see, if development
is the environment type, WP_DEBUG
is automatically set to true
(if not defined).
What is the difference between local and development?
The differences between staging and production are obvious. Staging should look and behave like production, but it’s a sign-off point for clients. Production is well, production. But what about local vs. development?
If you develop plugins locally like me, chances are your local and development environments are in the same place. However, when working on client sites, a development environment is typically hosted and is used for quality assurance and testing items that require a publicly accessible URL (such as device testing).
In this context, a “local” environment would be the environment on the developer’s computer. A “development” environment would be some type of hosted environment that can be used to troubleshoot and test live URLs.
Mika Epstein explains:
localhost = This is on my laptop and I may be using it on an airplane, so remote calls should not happen. This is like dev, only _potentially_ offline. (NB: We did call it localhost so it was 100% clear it was all local all the time, and something familiar to our ESL devs) (Source)
Mika Epstein
Mark Jaquith elaborates:
[local vs development]I am online, but my local site is not accessible on the public web. (Source).
An example is a caching plugin that connects to Cloudflare. It would make sense to enable Cloudflare cache on a hosted development environment but not on a “local” environment. Other plugins like Jetpack that require a live URL for some services to work would also be able to make use of “local” to indicate that the plugin shouldn’t try to communicate with its hosted services.
"local" only
Use “local” if you are indicating offline status or want to prevent communication with third-party APIs
For clarity, if you are developing locally and may not be connected to the net or want to indicate offline behavior, use local
as your environment type. Otherwise, development
should get you by most of the time.
Setting environment variables
For most cases, you can get by setting environment variables using wp-config.php
. If you’d like to go the .env route, I recommend this great article on setting environment variables using WordPress.
The benefits of using a .env file are that you keep important credentials for databases, payment gateways, etc. out of your document root, out of your database and thus have a more secure setup, with a single configuration file.
WP_ENVIRONMENT_TYPE vs. getenv
PHP function getenv can also get environment variables, but using WP_ENVIRONMENT_TYPE
is preferred here since getenv
is not always available or preferred.
WordPress function wp_get_environment_type is also a wrapper for getenv
as well (shown below), so if you are getting an environment type, it’s best to use the core function.
if ( function_exists( 'getenv' ) ) {
$has_env = getenv( 'WP_ENVIRONMENT_TYPE' );
if ( false !== $has_env ) {
$current_env = $has_env;
}
}
// Fetch the environment from a constant, this overrides the global system variable.
if ( defined( 'WP_ENVIRONMENT_TYPE' ) && WP_ENVIRONMENT_TYPE ) {
$current_env = WP_ENVIRONMENT_TYPE;
}
Code language: PHP (php)
WP_DEVELOPMENT_MODE and its uses
This brings us to the latest constant coming in WordPress 6.3, which is WP_DEVELOPMENT_MODE. Let’s see what this constant provides and how we can use it going forward.
Current valid modes are:
- core
- plugin
- theme
- all
It would be defined in the wp-config.php
file:
define( 'WP_ENVIRONMENT_TYPE', 'development' );
define( 'WP_DEVELOPMENT_MODE', 'plugin' );
Code language: PHP (php)
Why is there a need for WP_DEVELOPMENT_MODE?
The need arose from themes and the theme.json file. In order to keep things flexible in block themes, theme.json is used to set all kinds of settings and variables. Reading this in every load and configuring and setting options would slow things down a lot, so this file is only read in periodically. In other words, it is cached.
If you’re developing in theme.json, you’d have to clear your cache every time you make a change to the file. In order to get around this, you can currently use WP_DEBUG
to indicate that you’d like this cache cleared every time.
The current workaround is to use WP_DEBUG. In function wp_get_global_stylesheet, there is currently this variable that determines if it returns a cached result or not:
$can_use_cached = empty( $types ) && ! WP_DEBUG;
Code language: PHP (php)
Here’s the thing: WP_DEBUG shouldn’t be used to change behavior. Having something enabled or disabled is an incorrect usage. Environment type is problematic here as well. You’d definitely want things cached on staging and production, but is it a valid assumption that caching should be disabled on development and local as well?
Should WP_DEBUG be combined with WP_ENVIRONMENT_TYPE to clear the theme cache? For example?
if ( defined( 'WP_DEBUG' ) && WP_DEBUG && 'local' === wp_get_environment_type() ) { ... }
Code language: PHP (php)
This assumes that theme development takes place when WP_DEBUG is enabled and “local” is the environment type. But what if you’re doing plugin development? You don’t need the theme.json cache cleared every time. That slows down your environment further.
By declaring a development mode, you can explicitly tell WordPress, “Yes, I am in development mode. Yes, I’m developing a theme.”
The above cache code could be rewritten a bit more explicit:
$can_use_cached = empty( $types ) && ! wp_is_development_mode( 'theme' );
Code language: PHP (php)
This makes it a bit more clear that cache isn’t used when in the “theme” development mode.
For adding more modes, it is being discusssed in Trac.
As far as WP_DEBUG and WP_DEVELOPMENT_MODE together, you could use these two complementary. The following will throw a fatal error if WP_DEBUG is on and development mode is in “plugin” mode:
<?php
function dlx_get_certain_post( $post_id ) {
// Get the post.
$post = get_post( $post_id );
// If no post, return debug notice.
if ( ! $post ) {
if ( dlx_can_throw_fatal_error() ) {
throw new \Exception( __( 'No post found.', 'dlx' ) );
} else {
return new \WP_Error( 'no_post', __( 'No post found.', 'dlx' ) );
}
}
// do more stuff...
}
/**
* Check if we can throw a fatal error.
*
* @return bool
*/
function dlx_can_throw_fatal_error() {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG && wp_is_development_mode( 'plugin' ) ) {
return true;
};
return false;
}
//dlx_get_certain_post( 0 );
Code language: PHP (php)
wp_is_development_mode function
In order to determine if WordPress is in development mode, you can use the new function wp_is_development_mode.
/**
* Checks whether the site is in the given development mode.
*
* @since 6.3.0
*
* @param string $mode Development mode to check for. Either 'core', 'plugin', 'theme', or 'all'.
* @return bool True if the given mode is covered by the current development mode, false otherwise.
*/
function wp_is_development_mode( $mode ) {
$current_mode = wp_get_development_mode();
if ( empty( $current_mode ) ) {
return false;
}
// Return true if the current mode encompasses all modes.
if ( 'all' === $current_mode ) {
return true;
}
// Return true if the current mode is the given mode.
return $mode === $current_mode;
}
Code language: PHP (php)
The function takes in the development mode (‘core,’ ‘plugin,’ ‘theme,’ or ‘all’) and returns true
if in development mode, but false
if not.
wp_get_development_mode function
Function wp_get_development_mode will return the type of mode WordPress is in.
/**
* Retrieves the current development mode.
*
* The development mode affects how certain parts of the WordPress application behave,
* which is relevant when developing for WordPress.
*
* Development mode can be set via the `WP_DEVELOPMENT_MODE` constant in `wp-config.php`.
* Possible values are 'core', 'plugin', 'theme', 'all', or an empty string to disable
* development mode. 'all' is a special value to signify that all three development modes
* ('core', 'plugin', and 'theme') are enabled.
*
* Development mode is considered separately from `WP_DEBUG` and wp_get_environment_type().
* It does not affect debugging output, but rather functional nuances in WordPress.
*
* This function retrieves the currently set development mode value. To check whether
* a specific development mode is enabled, use wp_is_development_mode().
*
* @since 6.3.0
*
* @return string The current development mode.
*/
function wp_get_development_mode() {
Code language: PHP (php)
It can return ‘core,’ ‘plugin,’ ‘theme,’ ‘all,’ or an empty string. An empty string should signify that WordPress is not in development mode.
Let’s take a plugin license check as an example. I’d like to ping my licensing API, but if I’m in local or development mode and developing a plugin, I don’t want to ping my licensing server. Here’s an example function I could call to prevent license server calls:
<?php
/**
* Check license status.
*
* @package DLXPlugins
*/
function should_check_plugin_license() {
// Ignore license checks on local/development environment and plugin development mode.
$environment_type = wp_get_environment_type();
if ( in_array( $environment_type, array( 'local', 'development' ), true ) && wp_is_development_mode( 'plugin' ) ) {
return false;
}
return true;
}
Code language: PHP (php)
I’m using the new WordPress function wp_is_development_mode to check if I’m in the plugin
development mode.
Conclusion
Within this article, I went over the various debug features that come with WordPress. I also went over the differences between environment types and development modes.
Do you think others will find the new development mode useful? Please let me know in the comments.
Like this tutorial? There's more like it. Subscribe today!

Ronald Huereca founded DLX Plugins in 2022 with the goal of providing deluxe plugins available for download. Find out more about DLX Plugins, check out some tutorials, and check out our plugins.