WordPress Architecture Fundamentals: OOP vs Procedural - Which wins?

Dive into the benefits, and considerations of both coding methodologies, and empower yourself to make a well-thought-out decision that aligns with your project's scope, team dynamics, and long-term goals.

tl;drGitHub

Sometimes, people don't think about what they're doing. I've seen crap like 5000 line files looking like copy and paste from StackOverflow not once. If it works, then it works, right? 🤷‍♂️ I agree with this, but the question is how does it work? I remember such cases really well because it was me who wrote this. I learned then that even the most basic decisions made without careful consideration can make the product suffer.

So today I'll talk about the most basic decision that needs to be made before starting any project: "Procedural or Object-Oriented? Which should be chosen?" I'll discuss the advantages and trade-offs of each, and provide basic concepts that should help you to make careful decisions on the winning strategy for your WordPress projects.


To start addressing the business needs in my application, let's focus on displaying links to the five most recently added teams after post content. To break down this requirement into manageable tasks, I'll start by registering a Team post type. Then, I'll write a method to fetch and display links to the five most recently added teams, ensuring they appear right after a post's content.

Image

This methodical breakdown helps streamline development and ensures each component functions as intended. In the next steps, I'll discuss how I would achieve those results using different programming approaches: procedural and object-oriented.


Procedural Approach in WordPress

Functionality is organized in a linear manner, often implemented in files like theme's functions.php. Involves defining functions or classes in the global scope, making them accessible throughout the app. Focuses on the simplicity and fast implementations, reducing the meaning of clear boundaries and separated responsibilities.

Image

To meet the goal I need to create two functions in the theme's functions.php file. I prefix them to avoid naming collisions with other themes, plugins or the core.

function fm_register_teams_cpt(): void
{
    register_post_type('team', [
        'labels' => [
            'name' => __('Teams', 'fm'),
            'singular_name' => __('Team', 'fm'),
        ],
        'public' => true,
    ]);
}

add_action('init', 'fm_register_teams_cpt');

function fm_add_teams_to_posts_content(string $content): string
{
    $teams = get_posts([
        'post_type' => 'team',
        'posts_per_page' => 5,
    ]);

    if (! empty($teams)) {
        $links = [];

        foreach ($teams as $team) {
            $links[] = sprintf('<a hre="%s">%s</a>', get_permalink($team), get_the_title($team));
        }

        $content .= sprintf(__('Latest Teams: %s', 'fm'), join(', ', $links));
    }

    return $content;
}

add_filter('the_content', 'fm_add_teams_to_posts_content');

If you choose this approach please do not put everything in single file! It makes the code organization, readability and reusability hell. Try to use Seaparation of Concerns to split the code to several files.

require_once(FM_PATH . '/app/posts.php');
require_once(FM_PATH . '/app/teams.php');

When it's worth to use this approach?

  • Recommended choice for beginners as it does not demand a high level of knowledge. Makes understanding how does the WordPress work easier.
  • Effective in solving straightforward problems or when fast implementation needed.
  • A common pitfall is tendency to write code without clear boundaries, leading to long and too complex files what makes the maintaining and modifying the codebase over time challenging.
  • Collaboration might be difficult due to potential problems of working together on the same file even with GIT merging tools.

Object-Oriented Approach in WordPress

Functionality is organized into modular and reusable objects or classes. These objects encapsulate data and behavior, promoting a more structured and organized codebase. OOP focuses on creating well-defined boundaries and separating responsibilities, allowing for easier maintenance, scalability, and code reusability.

Image

At first, I need to use separation of concerns and define modules that will be used then to build the whole application. Teams which will be responsible for managing team type behaviour like creating CPT, and similar Posts module which will handle displaying tags in the post content and manage other post type behaviour.

namespace FM\Teams;

class Teams
{
    public function __construct()
    {
        add_action('init', [$this, 'initCPT']);
    }

    public function initCPT(): void
    {
        register_post_type('team', [
            'labels' => [
                'name' => __('Teams', 'fm'),
                'singular_name' => __('Team', 'fm'),
            ],
            'public' => true,
        ]);
    }
}
namespace FM\Posts;

class Posts
{
    public function __construct()
    {
        add_filter('the_content', [$this, 'addLinks']);
    }

    public function addLinks(string $content): string
    {
        $teams = get_posts([
            'post_type' => 'team',
            'posts_per_page' => 5,
        ]);

        if (! empty($teams)) {
            $links = [];

            foreach ($teams as $team) {
                $links[] = sprintf('<a hre="%s">%s</a>', get_permalink($team), get_the_title($team));
            }

            $content .= sprintf(__('Latest Teams: %s', 'fm'), join(', ', $links));
        }

        return $content;
    }
}

Once I have modules, I need to connect them at some place called Facade. It acts as a central entry point for initializing and accessing system components, and delegating complex work to others. It simplifies setup, reduces code complexity, and provides a unified interface to interact with underlying systems.

namespace FM;

use FM\Posts\Posts;
use FM\Teams\Teams;

class App
{
    public function __construct()
    {
        new Posts();
        new Teams();
    }
}

That's simple facade because it doesn't delegate any work yet, but I'll handle it in the next chapters. It's also important to add that you've just met the first design pattern.

When I need to add a new module, I simply create and initialize it without having to modify any of the existing modules. This modularity also simplifies troubleshooting; if there's an issue, I can isolate the problematic module by commenting out specific initialization lines to see if the problem persists. It's like connecting another LEGO structure with own behaviour to the whole building.

Lego

One of the key points in this approach is separating responsibilities which result in more files to load. No worry, you won't need to dive into the files loading hell. Just check out the previous article and let the Composer do it for you!

When it's worth to use this approach?

  • Better approach for solving complex problems and building medium/large apps.
  • Improves code readability by representing real-world objects and their interactions, making the code more intuitive and easier to understand or debug. It's easier for me to read $team->hasName() instead ! empty(fm_team_get_name()).
  • Ensures that internal details are not available publicly.
  • Promotes reusable components, enhancing code maintainability and reducing redundancy. It makes the teamwork easier, because it allows to work on different parts of the code by many developers without struggling with conflicts.

What is a real-world example?

Mark, the founder of an IT company, managed everything by himself in the early stages - development process, client relationships, and finances, and many more. This was easy at the time because the company was relatively small.

Image

As the company grew, the workload became overwhelming for Mark, prompting him to expand his operations. He leased an open space and hired additional staff to manage different aspects of the business. This shift allowed Mark to delegate responsibilities and enabled the company to take on more projects. While the expansion brought some improvements, it also introduced new challenges.

  • The teams began to disrupt each other's. For example, when the finance team held loud discussions about rates, it disturbed the concentration of other team members.
  • Mark's commitment to transparency led to unintended consequences. When Bill inquired about Sarah's salary, Mark disclosed it, which affected team morale.
  • Despite the delegation, Mark still found himself heavily burdened. He felt a direct responsibility for each team member, which kept his stress levels high.

Mark started realising that the more people he have, the more new problems needed to be handled. He ran the company in a chaotic and centralized way. Everything depended on him so he decided that some changes are needed!

Image

  • Reorganization: He moved the company to a new office with separate rooms for each team, along with a dedicated meeting room. This physical separation minimized disruptions, allowing teams to focus and enhancing overall productivity.
  • Delegation: Mark hired leaders for each team, empowering them to manage day-to-day operations and strive towards the company's objectives. This shift allowed Mark to step back from micro-managing, focusing on strategic growth.
  • Transparency: Realizing that too much openness could lead to friction, Mark revised his transparency policy. Sensitive information, particularly financial details, was restricted to authorized personnel only. This helped maintain trust while preventing unnecessary conflicts and preserving operational efficiency.

Decentralisation improved company efficiency and opened the doors for more challenging clients! Teams became self-sufficient, micromanagement was reduced, and problems started to become visible in the early stages which allowed react faster. When one team became unnecessary, Mark was able to close it without bothering others.

How this story can help you decide?

Can you see the analogy of the story to two described programming methods? In my opinion it's just a story of transition from procedural to object-oriented coding.

  • Procedural: The initial setup of Mark's company was akin to writing procedural code, where various functions were placed in the global scope without clear boundaries. As the project scaled and more people joined, this lack of structure led to chaos. Simple changes in one area could affect other parts of the organization. Having multiple developers working in the same environment compounded these issues, making it difficult to manage changes and maintain a smooth workflow.
  • Object-Oriented: Mark's change adopted principles similar to the ones from object-oriented programming. He organized his company into separated teams, each with clear responsibilities and team leaders. He rencapsulated sensitive information, which protected critical data and enhanced operational integrity. This approach significantly reduced inefficiencies and improved the company's scalability.

Mark started the company with growth in mind so decentralized and OOP approach was the best choice since beginning. Building a business would take more time initially, but it would reduce the cost of change in long term. And that's what I like.

It can be compared to our clients. They come to us and invest money in solutions that will let them GROW. I've never had a client who said that they pay such an amount of money just to sell the same number of products as now. It doesn't make sense. That's why I believe that one of the most certain things in business is CHANGE.

So even when I don't plan to rule the world in the first week of running a product, I'm sure that at some point it should change. So if I can be better prepared to this from the beginning, I'll take this. That's why I'm choosing the OOP approach by default. It makes me better prepared for change.


Procedural or Object-Oriented Approach?

Both approaches have their pros and cons, and the decision ultimately rests on your own preferences and current needs. Regardless of which option you choose, it's important to understand why you choose this particular one.

  • Procecural: If you believe that you will be the sole decision-maker and won't be working in a team, or if you're working on a simple project where advanced architecture isn't necessary, then the procedural approach might be the right fit for you. It offers simplicity and straightforwardness.
  • Object-Oriented: If you're working on a product that will have a wide user base and a long lifespan, or if you plan to build a larger team to handle the project over time, then investing time in the object-oriented approach could be worth. It provides a structured and scalable solution.

I recommend OOP approach by default because it fits my needs, but when it comes to you, I don't really know! You should decide! 😂 To help you decide, I've created a simple table with subjective scores based on my personal experience, habits and problems I was facing. It doesn't mean that one is always better than other.

TermProceduralOOP
Complexity
How easy it is to feel confident and create code without a high level of technical knowledge
43
Readability
How easily the code can be understood and interpreted by the people.
35
Modularity
How easily smaller, independent components can be developed, modified, and maintained separately.
25
Maintenance
How eaisly the code can be modified and extended over time, minimizing errors and reducing the effort required for maintenance.
34
Teamwork
How eaisly the code can be managed by many developers at once.
24
14/2521/25

Feedback

How satisfied you are after reading this article?