BDShield

A twelve-plugin WordPress suite with a shared licence server and self-hosted updates.

A WordPress admin dashboard on a laptop.

BDShield is a suite of twelve WordPress plugins, sold and supported at getbdshield.com. Backups, an activity log, uptime monitoring, image optimisation, a firewall, and others.

This page is about the shared architecture underneath them, not about what any one of them does.

The constraint

Twelve plugins is not twelve projects. It is one project with twelve deployment targets, and the difference decides whether a small team can maintain it.

The second constraint is that the plugins run on hosting we do not control. The production site itself taught us most of this: it runs LiteSpeed, where custom HTTP headers set through .htaccess are stripped, so any feature that depended on mod_headers had to be redesigned. ZipArchive is not available on that host, so the backup plugin uses PclZip. Web PHP is 8.3 and CLI PHP is 8.5, which is the kind of split that makes a cron job behave differently from the same code in an admin request.

The stack, and why

PHP against the WordPress plugin API, with a deliberately narrow set of patterns repeated across all twelve.

Every plugin has the same skeleton. Constants for version and paths, a top-level defaults function, activation and deactivation hooks registered at file scope, a final singleton class with instance(), includes() and hooks(), and a global accessor function. Settings live in one serialised array in wp_options per plugin, read through a helper that merges with defaults. Tables are named {prefix}_{plugin}_{feature} and created by dbDelta() in a static method called from the activation hook. Admin screens are a singleton in admin/class-*-admin.php with views in admin/views/.

None of that is clever. That is the point. Reading the fourth plugin costs nothing once you have read the first, and a fix found in one is mechanically applicable to the other eleven.

a grid of twelve small identical plugin tiles at left, each showing the same three stacked labels `singleton / settings array / dbDelta tables`.

Three decisions

One updater file, copied not abstracted. class-bd-auto-updater.php is byte-identical across the plugins. It hooks pre_set_site_transient_update_plugins and plugins_api and checks a REST endpoint on our licence server. We could have shipped it as a composer package or a must-use plugin. We did not, because a shared dependency between twelve independently installable plugins on somebody else's site is a support burden we would carry forever. The cost is twelve copies to update in lockstep, which is a scripted change. The benefit is that no plugin can break another one.

We host the licence server ourselves. bdshield-license is a WordPress plugin on our own commerce site exposing POST /wp-json/bdls/v1/validate for activation and deactivation, and a check-update endpoint the auto-updater calls. Client-side, each plugin has its own licence class that caches the result in a transient for a day and answers is_active(), get_tier() and has_feature(). Rate limiting is 30 requests a minute per IP.

Gating is enforced at multiple points rather than one. In BD Auto Blog that is seven separate checks across the scheduler, the writer, the helpers and the admin screens. A single gate is a single line to comment out.

Deployment is a Python script per plugin. Each has a redeploy.py that pushes over SFTP with paramiko, and every final build is copied to one canonical production folder. This is not sophisticated and it does not need to be. What it does is remove the step where somebody drags a folder into cPanel and forgets one file.

What broke

The dark admin theme did not reach the edges of the screen. Thin white strips down the left and right of every plugin's dashboard.

Several changelogs already claimed this was fixed. It was not. The earlier patches set margin: 0 on the wrapper, which does nothing about WordPress's own #wpcontent gutters. One plugin was worse: it capped the element that painted the background at max-width: 1200px, so on any wide monitor the theme simply stopped.

The canonical fix pulls the painted wrapper into the gutter with a negative left margin, sets a minimum height against the viewport, and puts the max-width on the wrapper's children instead of the wrapper. One rule came out of it and it applies well beyond WordPress: never put a max-width on the element that paints the background.

We shipped that across all twelve in one pass, along with a single colour palette replacing four different ones that had drifted apart, and a set of small fixes each plugin had been carrying alone. A related one from the same sweep: the speed plugin's HTML minifier was corrupting JSON-LD, because it did not protect <script> and <pre> content before collapsing whitespace.

What we would do differently

Two things.

The scheduled work is inconsistent. Some plugins use wp_schedule_event() and some use a self-rescheduling single event, and the choice was made per plugin rather than once. Both clear their hooks on deactivation, so nothing leaks, but a reader has to check which pattern they are in. That should have been one rule.

The other is the settings blob. One serialised array per plugin in wp_options is fast to write and awkward to migrate, and every schema change now needs a merge-with-defaults path that behaves correctly for a site that skipped four versions. It was the right call at plugin one and it is a small tax at plugin twelve.

Where it is now

All twelve are live and on sale at getbdshield.com, on one architecture, one palette and one updater. The suite is sold direct rather than through the WordPress.org repository, which is a distribution decision and not a technical one.

Built by BestDid

We build and maintain the BDShield plugins. Licences, tiers and documentation are at getbdshield.com.

Elsewhere on the shelf: the Toivo case study covers structured data discipline on a static site, and the XWipe case study covers building against a rate-limited third-party API. The full list is on our work page. To discuss a build, see services.

Visit BDShield