How to Write Better WordPress Code with Dependency Injection in 30 Days?

Think Like an Architect

Understanding DI intellectually is easy. Applying it consistently? That requires rewiring your brain. Here’s how to transition from “WordPress hacker” to “software architect.”

Step 1: Adopt the “New is Glue” Mantra

Tattoo this phrase on your brain:

“New is Glue.”

Every time you type new ClassName() inside another class, you’re supergluing those two classes together forever.

The Habit:

Before writing new, pause and ask yourself:

“Should I be creating this object here, or should someone else create it and pass it to me?”

New is Glue - Dependency Injection

Example:

// BAD: Creating dependencies inside the class
class EmailSender {
    public function send() {
        $mailer = new PHPMailer();  // GLUE!
        $logger = new FileLogger();  // MORE GLUE!
    }
}

// GOOD: Inject dependencies
class EmailSender {
    public function __construct(
        MailerInterface $mailer,
        LoggerInterface $logger
    ) { ... }
}

Step 2: The 15-Minute Planning Ritual

You cannot code architecturally in a rush. Professional developers plan before they type.

The Daily Routine:

Before touching your keyboard, spend 10-15 minutes with a piece of paper (yes, actual paper):

  1. Draw your classes as boxes: One box per class you’ll need.
  2. Draw arrows for dependencies: If Class A needs Class B, draw an arrow from A to B.
  3. Check for circular dependencies: If you see a circle (A needs B, B needs A), STOP. Redesign before coding.
  4. Identify what to inject: For each class, write down what it needs in its constructor.
The 15-Minute Planning Ritual : Dependency Injection

This 15-minute investment saves hours of refactoring later. You’ll catch design flaws before they’re immortalized in code.


Step 3: Start Small, Then Scale

Don’t try to refactor your entire codebase overnight. Start with one class.

Your First DI Win (10-Minute Exercise):

  1. Find a class that uses global $wpdb
  2. Add a constructor that accepts wpdb as a parameter
  3. Store it in a protected property
  4. Replace all global $wpdb calls with $this->db
  5. Update the instantiation to pass $wpdb

Congratulations, you just wrote your first dependency-injected class. Do this once a day for a week, and it becomes second nature.


Step 4: Graduate to a Dependency Injection Container

Initially, manually wiring dependencies is fine:

$mailer = new PHPMailer();
$logger = new FileLogger();
$sender = new EmailSender( $mailer, $logger );

But as your application grows, this becomes tedious. Enter: Dependency Injection Containers (DIC).

Popular PHP DI Containers:

  • PHP-DI: The most powerful and feature-rich
  • Pimple: Lightweight and simple
  • Acorn (Roots Sage): Built into modern WordPress theme frameworks

A container automatically resolves dependencies. You register your classes once:

$container->set( EmailSender::class, function() {
    return new EmailSender(
        new PHPMailer(),
        new FileLogger()
    );
});

Then retrieve fully-wired instances anywhere:

$sender = $container->get( EmailSender::class );

Step 5: Think in Contracts (Interfaces)

The final mindset shift: stop thinking about concrete classes. Start thinking about contracts (interfaces).

Instead of requiring a specific PHPMailer class, require any class that can send mail:

interface MailerInterface {
    public function send( $to, $subject, $body );
}

class EmailSender {
    public function __construct( MailerInterface $mailer ) {
        // Accepts ANY mailer: PHPMailer, SendGrid, Mailgun, etc.
        $this->mailer = $mailer;
    }
}

Now you can swap mail providers without touching EmailSender:

// Today: PHPMailer
$sender = new EmailSender( new PHPMailer() );

// Tomorrow: SendGrid
$sender = new EmailSender( new SendGridMailer() );

// Next week: Your custom solution
$sender = new EmailSender( new CustomMailer() );

This is the ultimate flexibility. You’re coding to contracts, not concrete implementations.


Advanced Mindset Techniques: From Good to Great

The “Dependency Audit” Weekly Exercise

Every Friday, spend 30 minutes reviewing code you wrote that week:

  1. Highlight every global keyword in your code
  2. Highlight every new keyword inside methods (not constructors)
  3. Ask yourself: Could these be injected instead?

Track your progress over time. Watch as your “global” count drops from 50 to 10 to zero.

The “Interface First” Design Pattern

Here’s a pro technique: write the interface before writing the class.

// Step 1: Define the contract
interface PostRepositoryInterface {
    public function save( $data );
    public function find( $id );
    public function delete( $id );
}

// Step 2: Implement it
class WordPressPostRepository implements PostRepositoryInterface {
    public function __construct( wpdb $db ) { ... }
    // Implementation details...
}

Why? Because interfaces force you to think about behavior before implementation. You design the API your code needs, then build toward it.

The “Composition Over Inheritance” Shift

Old-school WordPress developers love extending classes:

class MyPlugin extends SomeFramework { ... }

Modern developers prefer composition:

class MyPlugin {
    public function __construct(
        Framework $framework,
        Logger $logger
    ) { ... }
}

Inheritance says “is-a.” Composition says “has-a.” Composition gives you flexibility. Inheritance gives you chains and shackles.


Real-World WordPress DI Example: A Complete Plugin Class

Let’s put it all together. Here’s a real WordPress plugin class using Dependency Injection:

<?php
/**
 * Main Plugin Class with Dependency Injection
 */

interface CacheInterface {
    public function get( $key );
    public function set( $key, $value, $expiration );
}

interface LoggerInterface {
    public function log( $message, $level );
}

class MyAwesomePlugin {
    protected $db;
    protected $cache;
    protected $logger;

    /**
     * All dependencies declared upfront - crystal clear!
     */
    public function __construct(
        wpdb $database,
        CacheInterface $cache,
        LoggerInterface $logger
    ) {
        $this->db     = $database;
        $this->cache  = $cache;
        $this->logger = $logger;
    }

    public function process_user_data( $user_id ) {
        // Try cache first
        $cached = $this->cache->get( "user_{$user_id}" );
        if ( $cached ) {
            return $cached;
        }

        // Fetch from database
        $data = $this->db->get_row(
            $this->db->prepare(
                "SELECT * FROM {$this->db->prefix}my_table WHERE user_id = %d",
                $user_id
            )
        );

        // Cache it
        $this->cache->set( "user_{$user_id}", $data, 3600 );

        // Log it
        $this->logger->log( "Processed user {$user_id}", 'info' );

        return $data;
    }
}

// Bootstrap the plugin
function bootstrap_my_plugin() {
    global $wpdb;

    $cache  = new WordPressCache(); // Implements CacheInterface
    $logger = new FileLogger();     // Implements LoggerInterface

    $plugin = new MyAwesomePlugin( $wpdb, $cache, $logger );

    // Wire up WordPress hooks
    add_action( 'init', [ $plugin, 'process_user_data' ] );
}

add_action( 'plugins_loaded', 'bootstrap_my_plugin' );

What Makes This Beautiful:

  • Every dependency is explicit in the constructor
  • Easy to test (inject mock cache, mock logger)
  • Easy to swap implementations (Redis cache instead of WP cache? One line change)
  • Self-documenting (anyone can see what this class needs)
  • No hidden globals lurking in methods

Common Pitfalls and How to Avoid Them

Pitfall #1: “Service Locator” Disguised as DI

// This is NOT Dependency Injection!
class BadExample {
    public function do_something() {
        $container = ServiceLocator::getInstance();
        $db = $container->get( 'database' );
    }
}

This is just a global variable with extra steps. True DI means dependencies come in through the constructor or method parameters, not fetched from a global container inside methods.


Pitfall #2: Constructor Bloat

If your constructor has 10 parameters, you have a design problem:

// TOO MANY DEPENDENCIES!
public function __construct(
    $db, $cache, $logger, $mailer, $validator,
    $sanitizer, $formatter, $parser, $renderer, $compiler
) { ... }

Solution: Your class is doing too much. Break it into smaller, focused classes.


Pitfall #3: Injecting Concrete Classes Instead of Interfaces

// Brittle: Locked to PHPMailer
public function __construct( PHPMailer $mailer ) { }

// Flexible: Works with any mailer
public function __construct( MailerInterface $mailer ) { }

Always inject interfaces when possible. This is called “Dependency Inversion Principle” (the “D” in SOLID).


Your 30-Day Dependency Injection Transformation Plan

Ready to commit? Here’s your roadmap:

Week 1: Awareness

  • Days 1-3: Read this guide twice. Take notes.
  • Days 4-7: Review your existing code. Count globals and new keywords. Just observe, don’t change anything yet.

Week 2: Baby Steps

  • Days 8-10: Refactor one class. Remove one global. Add one constructor injection.
  • Days 11-14: Refactor three more classes. Share your code with a colleague for feedback.

Week 3: Practice

  • Days 15-18: Write a new feature using DI from the start. No globals allowed.
  • Days 19-21: Sketch dependencies on paper before coding. Make it a habit.

Week 4: Mastery

  • Days 22-25: Install a DI container (PHP-DI or Pimple). Wire up your plugin.
  • Days 26-28: Write your first unit test using mock dependencies.
  • Days 29-30: Write a blog post or tutorial teaching someone else what you learned.

By day 30, Dependency Injection will feel natural. You’ll wonder how you ever coded without it.


Additional Resources

Leave a Reply to Dependency Injection in WordPress: Complete Guide for Modern Developers in 2026 – Kishan JasaniCancel reply

One response to “How to Write Better WordPress Code with Dependency Injection in 30 Days?”

  1. […] “If you’re struggling with tight coupling, follow our 30-day guide to building a Dependency Injection mindset.” […]