Jim Welcome to Stop Repeating Yourself – Modularized WordPress Development Today we’re going to be talking about our strategies for large-scale WordPress site development, why we’re changing those strategies, and some tools and techniques we’re using to transition to more modularized development. #DPA5
Both Introduce ourselves briefly as web developers for UCF Web Communications (Webcom) Maybe note our level of experience with WordPress in general, or other experience that is relevant to the presentation?
Jo UCF Web Communications, or Webcom, consists of 4 total developers and 1 director As part of Webcom, we’re responsible for a large number of top-level websites and applications, as well as many sites for other departments, organizations, and colleges within the university Most of these sites run on WordPress
Jo Webcom currently develops and maintains 36 unique WordPress sites Just about all of them are built off of the same core set of code, with various tweaks and customizations Several sites from other departments are also built off of our code
Jim So, what does our code look like? According to WordPress best practices, we’re doing it the “wrong way”.
OOP approach to building common features Rapid creation of custom meta fields Rapid configuration changes Useful UI features for users
Run through the before/after of creating a custom post type.
Under our current theme development process, themes are treated like individual sites: all layout, styles and functionality and wrapped up in a single package. This can cause a lot of repetitive code across themes, especially if small customizations are needed to certain functional parts of the themes.
Let’s say we have five websites we need to build and support. The primary website is going to use a (transition) news feed, (transition) events feed, (transition) people custom post type, (transition) carousel, along with (transition) our layout and styles specific to the theme. All of this would be written within the theme code (transition).
This process would be repeated along the other 4 sites we need to support. (transition) Since each theme has some particular needs for the various functional groups, each code base will be slightly different and the original code base will gradually go out of sync.
Jim - While our current development process has certain benefits, it comes with several drawbacks. Being able to centralize (almost) all site logic and styles within a single repository makes it very easy to deploy site changes, and allows us to heavily customize existing functions and styles without modifying any other sites or code However, with the growth of our department and the number of sites we maintain, this development strategy has become a double-edged sword: If any piece of logic cloned off of our generic theme breaks, it must be fixed individually in every theme that contains that code. It also makes it very time-consuming to update any features that are repeated across multiple themes. Our themes are also not good examples of WordPress best practices. Ideally, we’d like to be a resource for well-written WordPress code for other departments, and for the open-source community in general.
Jim Talk about DRY as it relates to single projects, functions/classes Will I use this function more than once? Is part of my function repeated in other functions? Is the code more understandable split into separate parts? Expand the concept of DRY beyond a single project to talk about its implementation across projects
Jim Regardless, doing things the “WordPress Way” is not as easy as it seems.
One of the biggest pain points with developing complex sites for WordPress is its lack of integrated dependency management. If you were building, say, a Node project, you’d be able to define a set of dependencies for that project using a packages.json file, and simply run a `npm install` command to download those dependencies. WordPress doesn’t have any built-in way of defining a set of required plugins for a single site—they must all be installed manually. This makes it very difficult to automate deployment of site updates when themes and plugins are separated.
There are a few third-party solutions to the issue, but none of them quite work for us.
Composer – has some unideal setbacks, such as requiring all packages to have a composer.json file—this forces you to depend on third-party mirrors of WordPress itself and the public plugin directory (WordPress Packagist) so that dependencies have a composer.json file. Doesn’t account for premium plugins or public plugins not in the official WordPress directory which may not offer Composer support. TGM Plugin Activation Library – lack of official MultiSite support (at time of writing)
Another problem with separating functionality from themes is the necessity for heavy per-site customizations. We are frequently asked to build sites that vary visually and functionally quite a bit from previous projects—we don’t have the luxury of using a single template across most of the sites we build. When you start separating theme logic and plugin logic, you lose the option of “just throwing stuff in your functions.php file”.
Which leads to the concept of plugin bloat—generally you want to avoid having tons of plugins activated, mostly for performance reasons and to reduce the number of potential sources for bugs. With modularizing out functionality into plugins, it’s very easy to go overboard with the number of required plugins.
Reduce the amount of time it takes to distribute bug fixes
Consolidate features and logic across sites throughout the university
Modernize our code base bringing it more inline with current development trends, making it easier to modernize further in the future
Create a standard plugin dependency method for ensuring sites work as intended when pushed into production
Distribute our code across campus to developers and non-developers alike. Plug and play AND Customizable
Promote best practices across campus
Ideally, we want to move away from this…
And move toward a process like this: where discreet functional code is written into plugins (transition), creating a foundation on which we can build our themes. The specific layout and style still needs to be present (transition).
However, the individual differences between themes would be reduced to in our theme code by using action hooks and css overrides (transition) reducing the amount of overall code that needs to be maintained between sites (transition).
Step one: If we want to shift to using plugins as the foundation for site functionality, we need to figure out how to separate theme functionality from plugin functionality for projects moving forward.
As Jim mentioned earlier, best practices state that themes should provide look and feel, and plugins should provide functionality.
But it can be challenging to figure out exactly what goes where—especially when WordPress is very forgiving and allows you to put pretty much any plugin-related code in your theme. There’s also a lot of conflicting information online regarding putting code in plugins vs your functions.php file.
A good way of deciding what functionality goes where is to ask yourself, “if I put this code in a theme, would I miss the functionality after switching themes?”
This is our hard line between the separation of content vs presentation for our projects moving forward.
We’ve grouped various common features into more general categories, and this isn’t a total comprehensive list of features, but it covers our most commonly-used WordPress features and functionality.
This separation of concerns leaves us with a lot of new plugins to develop.
Obviously, we want these new plugins to work out-of-the-box for non-programmers, but we still need to be able to add theme-specific overrides in an elegant, modular way.
So, for reusability and portability, we decided that these new plugins that we’re developing would include some default set of styles and templates, but that those default styles could be disabled when more customization is desired via the theme.
For extensibility, these plugins would heavily utilize actions and filters to allow overriding of default templates. They would also include well-documented Sass partials, which could easily be imported into a theme’s Sass files during development.
We’re still in the process of developing the feature plugins we want to transition to using, but we do have a few good examples to show off.
Our UCF News and Events plugins provide shortcodes, widgets, and base functions for displaying content from our news site and university-wide events system.
The screenshots here show what both plugins would look like out-of-the-box with default styles and markup applied.
CSS provided by the plugins can be disabled for theme developers that want to include styles for the plugin content within their themes.
You can see here where we’ve provided an option within the WordPress admin to toggle styles on or off.
A default layout and styles are provided in each plugin, but new layouts are easy to add via actions and filters.
To show off how you can use actions and filters, here’s an example that shows how you can extend the UCF News plugin. You can see how we’ve added actions for defining parts of a “layout” (and, by “layout”, we mean the combination of markup and styles for a chunk of content). This code creates a new “layout” for news content called “masonry”, which would probably display news in a stacked grid layout. The masonry layout would be selectable as an option for the news widget and as a possible attribute value for the news shortcode.
This piece of code defines 5 new actions; the first four are run whenever a particular layout called “masonry” is being used to display news content. The functions passed to those actions, the “news_masonry_template_” functions, would print the markup before the news items, print the news items themselves, and print the markup after the news items, respectively. Each action is generated dynamically by the plugin, based on registered layout IDs.
The final line of code registers the “masonry” layout in the plugin’s available list of layouts. The “add_masonry_layout” function would push a layout ID and name to an array used by the plugin and return it.
I mentioned earlier that we have a lot of new plugins to build--in addition to the news and events plugins, we’re planning on building a suite of plugins for stuff that we tend to use frequently in our themes.
These range from basic WordPress content definitions, like post types and taxonomies, to plugins that replace some of the theme tools we mentioned earlier, like our shortcode GUI, to plugins that integrate with other services we manage, like our search service, weather feed, and campus map.
By building out plugins like this, particularly for existing services, we gain the added benefit of promoting our centralized data sources, and reducing data duplication across sites.
But we don’t plan on trying to build *everything* ourselves.
It’s not in our best interest to re-invent the wheel, especially when other developers have created tools that accomplish much of what we need already.
We are, however, pretty strict with the third-party plugins we install. Most undergo code reviews and testing by our team before being activated in our production environments.
Doing this helps us stay ahead of performance issues and potential bugs, and prevents us from wildly activating plugins and ending up with plugin bloat.
On the last slide, we mentioned using the Advanced Custom Fields plugin. In terms of modularization, ACF is pretty powerful—by defining meta box and meta fields separately from the custom post type plugins we’re building, our custom post types become much more flexible and reusable across sites.
When you’re creating a new custom post type, it’s easy to think of that post type as the combination of the post type itself and whatever custom meta boxes and fields you assign to it. Generally, one of the biggest reasons for creating a new post type is to be able to save some extra data to those posts that built-in post types aren’t capable of doing.
However, by using a plugin such as ACF to define meta fields for a post type separately, post type definitions become more reusable and scalable for a larger number of sites.
For example, one site could utilize a “Person” post type with meta fields for an email address and phone number, while another site could use the same “Person” post type but with a field for the person’s address instead. Both sites would use the same plugins but with different ACF configurations.
Again, the goal is to find the right balance between reusability and practicality. We’re looking to split out re-usable chunks of logic into plugins where we can, without ending up with too many moving dependencies and parts. *segue to Jim*
In conclusion, developing sites for WordPress in a DRY way requires a different approach to both code and the way you and your team approach site development.
Ultimately, despite some of the shortcomings of the WordPress ecosystem, it is possible to modularize your code and stop repeating yourself.
But your mileage may vary! There may be other techniques that work better for you and your team. We hope our experience with transitioning our codebase can provide some helpful insight into how your team approaches WordPress development.
If you’re interested in the work we’ve done so far, it’ll be available publicly on Github.
Stop Repeating Yourself: Modularized WordPress Development
Stop Repeating Yourself
MODULARIZED WORDPRESS DEVELOPMENT
Who We Are
Web Applications Programmer
4 years at UCF
Web Applications Programmer
4 years at UCF
HEW2016 — #DPA5 SLIDE 2
UCF Web Communications
• Part of UCF Marketing
• Team of 5 (4 developers, 1 director)
• Responsible for development of top-level websites and
applications for the university
• Develop and maintain sites for various departments,
organizations, and colleges
HEW2016 — #DPA5 SLIDE 3
Current Theme Development
• Most functionality provided for the site is defined in the theme
• New themes are based on a generic theme with helper functions
for faster development
reproduced with each new theme
• Minimal reliance on plugins
• Build sites, not themes
HEW2016 — #DPA5 SLIDE 5
Current Theme Tools
• Abstract classes for custom post types, custom taxonomies
• Support for adding custom meta fields to custom post types
• Support for quickly adding common configuration items
• Useful UI features, like a shortcode WYSIWYG GUI
HEW2016 — #DPA5 SLIDE 6
Custom Post Types
What You Need
• Singular Name
• Plural Name
• Standard Options
What You Get
• Registration Logic
• Name Generation
• Automatic Shortcode
• Metaboxes and Fields
HEW2016 — #DPA5 SLIDE 7
WordPress Best Practices
• Provide look and feel
• Page and post templates
look and feel
• Provide functionality
• Custom Post Types and
HEW2016 — #DPA5 SLIDE 12
Challenges with the “WordPress Way”
• Dependency management
• Heavy per-site customizations, in bulk
• Plugin bloat
• Minified asset delivery
HEW2016 — #DPA5 SLIDE 13
HEW2016 — #DPA5 SLIDE 14
Time to Distribute Bug Fixes
HEW2016 — #DPA5 SLIDE 15
Similar Features and Logic
Across Our Themes
Separate theme functionality from
HEW2016 — #DPA5 SLIDE 23
Theme, or Plugin?
• Theme: look and feel
• Plugins: content and functionality
If the code were to be placed in a theme, and the theme
was then switched out, would you miss its functionality?
• Yes: plugin code
• No: theme code
HEW2016 — #DPA5 SLIDE 24
Separation of Concerns
(Content vs. Presentation)
• Page and Post Templates
• Menu Locations
• Presentation-related functions
• Theme specific styles
• Overrides for plugin provided
markup, specific to theme
• Presentation Configuration
• Theme mods
• Data-related functions
• Data Definition
• Custom Post Types
• Meta Fields
• API Endpoints
• Data Configuration
HEW2016 — #DPA5 SLIDE 25
Reusability & Portability
• Default Styles and Templates
• Ability to turn off default CSS
and JS when more
customization is desired
• Customization of look and feel
through theme CSS
• Default functionality without
• Actions and Filters to allow
overriding of default templates
• Sass artifacts available in
repository for easy overrides
• Well documented CSS classes
for theme specific overrides
HEW2016 — #DPA5 SLIDE 26
Plugins We’re Building
• Post types and taxonomies
• Shortcode WYSIWYG interface
• Autocomplete search field for
lists of posts by type
• Plugin for general utility
Plugins for Services
• UCF search service
• Weather data
• Map data (map.ucf.edu)
HEW2016 — #DPA5 SLIDE 36
Plugins We’re Not Building
• Meta box and meta field management (Advanced Custom
• SEO optimization (WordPress SEO/Yoast)
• Form management (GravityForms)
• And a few others…
HEW2016 — #DPA5 SLIDE 37
Modularization of Plugin Functionality
Example – Separation of post type and meta field definitions
custom post type
Advanced Custom Fields
(or other meta field manager)
• Email field
• Phone number field
• Address field
HEW2016 — #DPA5 SLIDE 38
Utilize a plugin + theme dependency
HEW2016 — #DPA5 SLIDE 39
• Focused on:
• Layout (Templates)
• Dependency Management
• Fail gracefully
• Handle dependencies through deployment process
HEW2016 — #DPA5 SLIDE 41
• Allow for quick customization of look and feel
• Pick and Choose:
• Page templates
• Adjust SASS Variables
• Create documentation and labels
• Use WP CLI to create site, install theme enable plugins
HEW2016 — #DPA5 SLIDE 42
• More effective maintenance
and upgrade cycles
• More rapid development of
• Distributed functionality and
• Increased consistency across
• Up front investment of time
• Additional responsibilities for
documenting and testing
• Change in culture – being
product driven instead of site
• Balancing these transitions
with the need to get
production work done
HEW2016 — #DPA5 SLIDE 43
• D.R.Y. WordPress sites require a different approach to both
code and site development as a whole
• Code will be available on Github
HEW2016 — #DPA5 SLIDE 45
• WordPress best practices
• Dependency Management solutions
• WordPress hooks, actions and filters
• Separation of Concerns (WP
• Theme or Plugin? (WP
• Meta field management plugins
• UCF on Github
HEW2016 — #DPA5 SLIDE 46