Introduction
When you're preparing for a WordPress developer interview or evaluating candidates, you'll quickly discover that true expertise goes far beyond knowing how to install a plugin. WordPress interview questions: best practices explained helps both sides of the table understand what separates mid-level developers from senior engineers who can architect scalable, secure WordPress solutions. We've compiled practical guidance on the topics that consistently come up in technical interviews, the ones that reveal whether someone understands WordPress at a systems level or just knows their way around the admin dashboard.
Actions vs Filters: The Core of WordPress Extensibility
This is almost always the first question a senior engineer asks because it demonstrates whether you understand WordPress hooks fundamentally. Many developers conflate the two, but they serve distinctly different purposes in the WordPress ecosystem.
Filters modify data and return it. You hook into a filter, receive some input, transform it, and pass it back. A filter always expects a return value. The typical pattern involves multiple filters running in sequence, each one potentially modifying the data before it reaches its destination.
Actions perform tasks without returning values. You hook into an action, do something (send an email, log data, update the database), and that's it. Actions are fire-and-forget operations that don't expect a return value.
Here's where the distinction matters in real code:
// FILTER: Receives data, modifies it, returns it
add_filter( 'the_title', function( $title, $post_id ) {
if ( get_post_type( $post_id ) === 'product' ) {
$title = 'Product: ' . $title;
}
return $title;
}, 10, 2 );
// ACTION: Does something, no return value expected
add_action( 'wp_insert_post', function( $post_id ) {
$post = get_post( $post_id );
error_log( 'Post created: ' . $post->post_title );
// Maybe also: send notification, sync to external service, etc.
}, 10, 1 );
The interview follow-up reveals whether someone truly understands this: "What happens if you use a filter when you should use an action?" The answer is important. If you add a filter that doesn't return anything (or returns null), you've just eliminated the data from the pipeline entirely. That's a production bug waiting to happen. Conversely, trying to add an action to modify content will fail because actions don't return values.
Real-world scenario: You're building an e-commerce integration. When an order is created, you need to (1) modify the order status based on payment method (filter) and (2) send a notification to your accounting system (action). If you mix these up, either your status logic won't work, or your notification won't fire.
The Template Hierarchy: Navigation Without Guessing
WordPress developers encounter the template hierarchy constantly, but many treat it as magic rather than a deliberate system. Understanding it deeply is what separates someone who randomly tries `single.php`, `post.php`, and `index.php` from someone who can architect a solution before writing the first file.
The hierarchy follows a priority order. WordPress looks for templates in this sequence and uses the first one it finds:
- Most specific template (e.g., `single-product-featured.php`)
- More specific templates (e.g., `single-product.php`, `single-post.php`)
- Generic fallbacks (e.g., `single.php`, `archive.php`, `index.php`)
Here's the practical implication: if you have `single.php` and `single-post.php` in your theme, WordPress will always use `single-post.php` for posts. If you only have `single.php`, it handles all single content types. This design lets you override as granularly as needed without creating unnecessary files.
For a custom post type called "case_study," the hierarchy looks like:
single-case_study-{slug}.php(for a specific case study)single-case_study.php(for all case studies)single.php(generic single content)index.php(fallback)
The senior developer question here is: "How would you structure templates to avoid code duplication across multiple post types with similar layouts?" The answer involves understanding you can use get_template_part() to share partials across different template files, or use blocks and the new full-site editing hierarchy if you're on modern WordPress.
Real-world scenario: You're building a multi-brand site with custom post types. Instead of creating duplicate `single-brand-a.php` and `single-brand-b.php`, you create `single.php` that uses `get_template_part( 'parts/single', get_post_type() ) to load a shared partial with brand-specific data passed in.
Securing Forms: Beyond the Nonce
Form security in WordPress interview questions typically aims at revealing whether someone knows it's not just about nonces. A complete answer covers nonces, sanitization, validation, and escaping—each solving a different security problem.
Nonces prevent CSRF attacks. They're time-limited tokens that prove the request came from your site, not from an attacker's malicious link.
Sanitization cleans input data. It removes or escapes potentially dangerous characters before storing data.
Validation ensures data matches expectations. An email field should contain an email; a number field should contain numbers.
Escaping protects output. When displaying user data on the frontend, escape it to prevent XSS attacks.
Here's a secure form implementation:
// Display form with nonce
wp_nonce_field( 'save_product_details', 'product_nonce' );
// Handle submission
if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
// Verify nonce
if ( ! isset( $_POST['product_nonce'] ) ||
! wp_verify_nonce( $_POST['product_nonce'], 'save_product_details' ) ) {
wp_die( 'Security check failed' );
}
// Sanitize input
$product_name = sanitize_text_field( $_POST['product_name'] ?? '' );
$product_price = floatval( $_POST['product_price'] ?? 0 );
$product_description = wp_kses_post( $_POST['product_description'] ?? '' );
// Validate
if ( empty( $product_name ) ) {
wp_die( 'Product name is required' );
}
if ( $product_price <= 0 ) {
wp_die( 'Price must be greater than zero' );
}
// Safe to use in database now
update_post_meta( get_the_ID(), 'price', $product_price );
}
// Display data safely
echo wp_kses_post( get_post_meta( get_the_ID(), 'description', true ) );
The interview depth question: "What's the difference between `sanitize_text_field()` and `wp_kses_post()`?" The first removes all HTML tags; the second allows safe HTML tags (like `
`, ``) while filtering dangerous ones. Your choice depends on whether users should be able to format their input.
Custom Tables: When They're Actually Necessary
This question reveals whether someone defaults to WordPress's post and postmeta architecture thoughtfully or reflexively. The honest answer is: most of the time, you don't need custom tables. But sometimes you genuinely do.
Use postmeta when: Your data has a one-to-one or one-to-many relationship with a post. Product prices, inventory levels, and related post IDs belong in postmeta. WordPress handles the querying, caching, and cleanup for you.
Use custom tables when: You need to store high-volume transactional data, complex relationships, or data that doesn't logically attach to a post. Order logs, audit trails, and analytics snapshots often live in custom tables.
The practical test: Can you query your data efficiently with WP_Query or WP_Meta_Query? If yes, use postmeta. Will your data have millions of rows that you'll query by specific columns frequently? Custom table.
Real scenario: You're building a ride-hailing integration. Driver ratings, ride history, and location data could technically go in postmeta (with drivers as posts), but if you're filtering rides by date range, pickup location, and distance traveled multiple times per second, custom tables with proper indexes will perform exponentially better.
// Custom table definition for rides
CREATE TABLE wp_rides (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
driver_id BIGINT UNSIGNED NOT NULL,
passenger_id BIGINT UNSIGNED NOT NULL,
pickup_lat DECIMAL(10, 8),
pickup_lng DECIMAL(11, 8),
dropoff_lat DECIMAL(10, 8),
dropoff_lng DECIMAL(11, 8),
created_at DATETIME,
KEY (driver_id, created_at),
KEY (passenger_id, created_at),
FOREIGN KEY (driver_id) REFERENCES wp_posts(ID),
FOREIGN KEY (passenger_id) REFERENCES wp_posts(ID)
);
If you go this route, you'll need to handle migrations, backups, and data integrity separately from WordPress's built-in systems. That's the tradeoff.
Common Mistakes
We see these in interviews and in production code constantly:
- Forgetting filter return values: A filter that doesn't return the modified value will null out your data. Always return something.
- Using filters for side effects: Filters might run multiple times (caching, previews, etc.). Use actions for anything with side effects, not filters.
- Assuming template hierarchy without checking: Developers often create template files that never execute because they didn't account for the full hierarchy or plugin overrides.
- Nonce-only security: A nonce proves the request came from your site, but it doesn't validate the data itself. You still need sanitization and validation.
- Postmeta for everything: Shoving all data into postmeta because it's convenient, then wondering why queries are slow with millions of rows.
- Escaping at the wrong time: Escaping on input (sanitization) is good practice, but escaping on output is essential. Don't skip output escaping.
Key Takeaways
- Actions perform tasks without returning values; filters receive data, modify it, and return it. Use each for its intended purpose.
- Master the template hierarchy so you can navigate and override templates intentionally rather than by trial and error.
- Form security requires nonces for CSRF protection, sanitization for input cleaning, validation for correctness, and escaping for output safety—they're not interchangeable.
- Custom tables make sense for high-volume transactional data with complex relationships; postmeta is sufficient and safer for most WordPress data.
- Senior developers understand the tradeoffs of each architectural choice and make decisions based on scale, performance, and maintenance burden rather than convenience.






Comments
Loading comments…