Introduction

Today’s focus was on debugging WordPress effectively and understanding the core parts of WordPress development that every support engineer must know.

I began by learning how to turn on debugging safely using constants in wp-config.php, and then explored three powerful helper plugins, Query Monitor, WP Crontrol, and User Switching to track performance issues, manage cron events, and reproduce user-specific bugs.

After mastering debugging, I moved into core WordPress development:

  • creating a small plugin,
  • basic concept of creating plugins
  • custom post types
  • custom taxonomies
  • wp_query

This day tied everything together showing how WordPress works under the hood, and how to quickly troubleshoot, extend, and optimize any site with professional-level tools.


01. Defining Debugging Constants in WordPress

When working as a support engineer or developer, debugging is your best friend — but it should always be enabled safely. WordPress allows you to control what errors appear and where they’re stored using a few simple constants inside wp-config.php.

Add the following lines above the comment /* That's all, stop editing! Happy publishing. */:

define( 'WP_DISABLE_FATAL_ERROR_HANDLER', true ); // WP 5.2 +: show real fatal errors while developing 

define( 'WP_DEBUG', true ); // turn on WordPress debug mode 

define( 'WP_DEBUG_DISPLAY', false ); // hide errors from the browser 

define( 'WP_DEBUG_LOG', true ); // log all errors to /wp-content/debug.log 

define( 'SCRIPT_DEBUG', true ); // load unminified core CSS and JS files

What Each Constant Does

ConstantPurpose
WP_DISABLE_FATAL_ERROR_HANDLERDisables WordPress’ built-in “white screen protection,” so you can view actual fatal errors while developing. (Only use this on local or staging environments.)
WP_DEBUGEnables all PHP notices, warnings, and errors for WordPress and plugins.
WP_DEBUG_DISPLAYHides errors from the frontend or admin area to avoid breaking layouts.
WP_DEBUG_LOGSaves all errors to /wp-content/debug.log, which can be opened in any text editor.
SCRIPT_DEBUGForces WordPress to use non-minified core files, making it easier to trace JS and CSS issues.

Where to View the Log

Open the file:
example.com/wp-content/debug.log

You can also use a plugin such as Log Viewer or Error Log Monitor to read this log directly from the dashboard.

02. Helper Plugins for Debugging in WordPress

There are certain WordPress plugins that make it much easier to debug PHP errors, actions, filters, enqueued scripts and stylesheets, and even HTTP requests.
In this section, we’ll go over three essential ones Query Monitor, WP Crontrol, and User Switching and see how each helps during development and troubleshooting.

PluginPurposeHow I Used It
Query MonitorShows PHP errors, database queries, HTTP requests, hooks & actions, scripts, and performance bottlenecks.Checked for slow database queries and heavy hooks on page load.
WP CrontrolLets you view and manage cron events and scheduled tasks.Disabled duplicate or unnecessary cron jobs during testing.
User SwitchingQuickly switch between users without logging out/in.Reproduced role-specific bugs (Subscriber vs Admin) safely.

Query Monitor

Query Monitor adds a complete developer tools panel inside WordPress.
It lets you inspect almost everything happening behind the scenes:

  • Database queries (with execution time and source)
  • PHP errors and notices
  • Hooks and actions currently running
  • Enqueued scripts and styles
  • REST API and Ajax calls
  • HTTP API requests and responses
  • Block Editor blocks and capability checks

The plugin automatically adds a toolbar entry in the WordPress admin bar; click it to open detailed tabs for each section.

Why it’s useful
Query Monitor instantly shows slow database queries or heavy hooks, helping identify performance bottlenecks. It’s a must-have in every development or staging site.

Here’s a brief introduction about the plugin and what information it displays and where:

You can learn more on the plugin’s page

💡 Idea to Explore → Using Query Monitor to Identify Performance Bottlenecks

If your WordPress site feels slow or inconsistent, Query Monitor can help you see what exactly is taking time:

  1. Activate the plugin, open any front-end page, and then open the admin bar → “Query Monitor.”
  2. Go to the Queries tab:
    • Sort by Time to find the slowest SQL queries.
    • Look for repeating queries or ones touching custom tables.
    • Note the calling function (helps find which plugin/theme is responsible).
  3. Check Hooks & Actions → shows how many callbacks each hook has. Too many actions on init or wp_head can slow down loading.
  4. HTTP Requests tab → see external API calls that may delay page rendering.
  5. Scripts & Styles tab → identify plugins enqueueing large CSS/JS files on every page.
  6. Summary panel → total queries, peak memory usage, PHP execution time.

Goal:

  • Anything consistently over 0.05 seconds per query or memory use above 64 MB on simple pages is worth optimizing.

Example

WP Crontrol

WP Crontrol allows you to view, manage, and debug WordPress Cron events directly from the admin area.
From its interface you can:

  • View all cron events with their hooks, arguments, recurrence, and next-run time
  • Edit, delete, or immediately run any event
  • Add custom cron events or custom schedules
  • Bulk-delete unwanted events
  • Export cron events to a CSV file

Why its useful
This plugin helps verify whether scheduled tasks (like emails, imports, or backups) are running properly. A common cause of “nothing happens” issues in WordPress.

You can read more about WP Crontrol on its plugin page.

User Switching

User Switching lets you instantly swap between user accounts without logging out and back in.
Normally, testing as a specific user means resetting passwords and logging in manually; this plugin saves all that time.

Helpful when:

  • An issue appears only for specific users or roles (e.g., “Subscriber can’t see button”)
  • You need to test restricted content, membership, or checkout flows for different roles

Just click Switch To under any user in Users → All Users, and you’ll be logged in as them instantly. A single click restores your original admin session.

You can check more details on the User Switching plugin page.

I also went through (quickly) xDebug and JavaScript with Chrome DevTool for language specific debugging


After completing the debugging and helper plugins session, I took a short lunch break.
Once back, I moved on to the Basic Plugin Development module starting with how plugins are structured, how to register them properly, and how WordPress hooks (actions and filters) connect everything behind the scenes.


03. Basic Plugin Development – Quick Start

Before diving into the deeper APIs, I focused today on understanding how plugins are structured and how hooks connect everything inside WordPress.

I began with a quick hands-on experiment using an MU-plugin to understand how WordPress executes hooks, and then gradually explored how actions and filters form the core of every plugin.


Understanding Hooks with an MU-Plugin

Before building a regular plugin, I tested how WordPress triggers hooks internally by creating a small MU-plugin (Must-Use Plugin).
MU-plugins load automatically on every request, making them perfect for experiments and debugging.

Steps followed:

  1. Created a new folder: wp-content/mu-plugins/
  2. Added a file: wp-content/mu-plugins/hook-watcher.php

hook-watcher.php

<?php 
/** 
* Plugin Name: Hook Watcher (MU) 
* Description: Logs every hook that fires — for learning purposes only. 
*/ 

if ( ! defined( 'ABSPATH' ) ) exit; 

// Extremely noisy — use only on local environment 
add_action( 'all', function ( $hook ) { 
 error_log( $hook ); } 
);

With WP_DEBUG_LOG enabled, this script records every WordPress hook into /wp-content/debug.log.

What I Observed

Opening the log showed a long list of hook names like:


🧩 3. Hooks – Actions and Filters

Hooks are the backbone of WordPress extensibility. They allow plugins, themes, and even core to “talk” to each other.

TypeWhat It DoesExample
Action HookRuns custom code at a specific point.add_action( 'init', 'my_function' );
Filter HookModifies data before it’s displayed or saved.add_filter( 'the_content', 'my_filter' );

🟢 Actions → “Do something.”
🟢 Filters → “Change something.”


Testing a Global Filter

To understand how powerful filters are, I tried adding this inside my MU-plugin:

add_filter( 'render_block', function( $block ){ 
  return '<img src="https://picsum.photos/' . rand(200,400) . '" />'; 
});

Effect:
This replaces every rendered block on the site with a random image from Picsum.
It clearly shows how a filter can intercept and modify the final output before display.
(And yes, remove this after testing 😄)


💡 Ideas to Explore

1️⃣ What is the purpose of add_action() and how is it implemented?
add_action() attaches your custom function to a hook so it runs at a specific moment.

add_action( 'init', 'register_custom_post_type' );

Use when you want to run code (register a CPT, enqueue scripts, schedule tasks, etc.).


2️⃣ How does add_filter() work, and when would you use it?
add_filter() modifies or formats data before it’s output or saved.

add_filter( 'the_title', 'prefix_modify_title' ); 
  function prefix_modify_title( $title ) { 
  return '👉 ' . $title; 
}

Use when you need to alter text, markup, or data dynamically.


3️⃣ What’s the difference between do_action() and apply_filters()?

FunctionPurposeExample
do_action()Executes all callbacks added with add_action()do_action( 'init' );
apply_filters()Passes a value through all add_filter() callbacks and returns it$title = apply_filters( 'the_title', $title );
  • add_action()do_action() = execute code.
  • add_filter()apply_filters() = modify and return data.

4️⃣ How can you prioritize Actions or Filters?
Both accept a priority number (default is 10).
Lower numbers run earlier; higher numbers run later.

add_action( 'init', 'early_function', 5 ); 
add_action( 'init', 'late_function', 20 );

Use priority to control the order when multiple functions are attached to the same hook.


✅ Summary

  • MU-plugins are great for quick experiments and debugging.
  • add_action() runs your code at a specific point.
  • add_filter() modifies data before it appears.
  • do_action() and apply_filters() are what actually fire those hooks.
  • Priority lets you decide the order of execution.

Together, these form the core foundation of how every WordPress plugin works.

04. Custom Post Types and Taxonomies – Quick Run

With limited time left in the day, I did a short but practical run on Custom Post Types (CPTs) and Custom Taxonomies. Two features that make WordPress flexible beyond posts and pages.


1. Custom Post Types

A Custom Post Type lets you create new types of content. For example, Books, Projects, or Reviews, each with its own editor and structure.

You register a CPT using the register_post_type() function, usually inside a plugin or in functions.php.

Example:


function create_books_cpt() {
    $labels = array(
        'name'          => 'Books',
        'singular_name' => 'Book',
        'add_new_item'  => 'Add New Book',
        'edit_item'     => 'Edit Book',
        'menu_name'     => 'Books',
    );

    $args = array(
        'public'        => true,
        'has_archive'   => true,
        'rewrite'       => array( 'slug' => 'books' ),
        'supports'      => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
        'show_in_rest'  => true, // enables Gutenberg & REST API
        'labels'        => $labels,
        'menu_icon'     => 'dashicons-book-alt',
    );

    register_post_type( 'book', $args );
}
add_action( 'init', 'create_books_cpt' );

Key Arguments and Labels in register_post_type()

ParameterPurpose
labelsDefines all text displayed in the admin for this CPT (name, add new, edit, etc.).
publicMakes it visible on the frontend and admin menu.
has_archiveCreates an archive page like /books/.
rewriteDefines custom URL slug for the CPT.
supportsEnables features like title, editor, thumbnail, etc.
show_in_restEnables block editor and REST API support.
menu_iconSets a custom icon in the dashboard menu.

👉 In short: labels control the text users see; args control how the post type behaves.


2. Custom Taxonomies

A taxonomy groups related posts together. For example, genres for books or skills for projects.
WordPress includes two default taxonomies: Categories and Tags, but you can create your own.

Example:

function create_book_genre_taxonomy() {
    register_taxonomy(
        'genre',
        'book',
        array(
            'label'        => 'Genres',
            'rewrite'      => array( 'slug' => 'genre' ),
            'hierarchical' => true, // true = like categories, false = like tags
            'show_in_rest' => true,
        )
    );
}
add_action( 'init', 'create_book_genre_taxonomy' );

When to Create a Custom Taxonomy

Create a custom taxonomy when you need to organize or filter a custom post type beyond the default categories or tags.
Examples:

  • Book Genres for “Books”
  • Project Types for “Portfolio”
  • Locations for “Events”

✅ Summary

  • Custom Post Types let you add new content types (Books, Projects, etc.).
  • Key arguments: labels, public, supports, rewrite, show_in_rest, has_archive.
  • Custom Taxonomies group content within those post types.
  • Use them when default categories/tags don’t fit your structure.

05. WP_Query – Custom Queries in WordPress

I also did a quick review of WP_Query, the main class that controls how WordPress retrieves posts from the database.

What is WP_Query?

WP_Query is the core query class that WordPress uses to fetch posts, pages, or custom post types based on specific conditions — for example, only posts from a certain category, author, or date range.

It gives developers complete control over which posts are displayed and in what order.

Example:

$query = new WP_Query( array(
    'post_type'      => 'book',
    'posts_per_page' => 5,
    'orderby'        => 'date',
    'order'          => 'DESC',
) );

if ( $query->have_posts() ) {
    while ( $query->have_posts() ) {
        $query->the_post();
        the_title( '<h3>', '</h3>' );
    }
    wp_reset_postdata();
}

How WP_Query Differs from get_posts() and query_posts()

FunctionDescriptionWhen to Use
WP_QueryThe main, flexible class for custom queries. Returns a query object you can loop through safely.Use in custom templates, plugins, or shortcodes.
get_posts()A simplified wrapper for WP_Query that just returns an array of posts (not the full query object).Use when you only need raw post data and won’t run a loop.
query_posts()A deprecated approach that modifies the main query directly (can break pagination or global query).Avoid using this. Prefer pre_get_posts or a custom WP_Query.

✅ Summary

  • WP_Query = the main and most flexible query class in WordPress.
  • get_posts() = lightweight shortcut, returns post arrays only.
  • query_posts() = modifies the main query and should generally be avoided.

In short:

Use WP_Query when you want full control, get_posts() when you just need data, and skip query_posts() entirely.

And then I scrolled through (Core APIs):

  • Metadata API
  • Shortcode API
  • Administration Menus
  • Settings & Options API

If you are up to learn deep plugin development you can learn them from WordPress codex.

Then I created a plugin folder structure using the WordPress Plugin Boilerplate Generator which was suggested in the assignment section.

With that I completed the quiz as well.