Looking for work with a US company? Apply to the Rolemote talent roster — free →

The brief they were screened against

Role: WordPress Developer · Band: $1,000–1,700/mo

Must-haves: maintains and extends existing WordPress sites, reads the code before changing it, child themes and staging-first updates, PHP and plugin-conflict debugging

Nice to have: WooCommerce, performance work (caching, Core Web Vitals), custom Gutenberg blocks

Scored by the same rubric the production screener runs on real applicants (junior-developer, claude-sonnet-4-6), on 2026-09-23. Nothing below was edited afterwards — including where it went against a candidate. The rubric and weights are published.

Miguel S.

SCREEN 88/100

Philippines · asking $1,400

Screening notes

The work sample is the standout: the candidate supplies unprompted detail that the question did not provide — the specific hook (woocommerce_package_rates), the freight-class field as the data gap, the 214 products, the cent-level shipping verification, and the proactive PHPCompatibilityWP sweep that found four additional deprecation notices, all of which are exactly the kind of 'what I found beyond the obvious' that separates a practitioner from someone describing a process they read about. The scenario answer is methodical and names real tools (wp plugin verify-checksums --all, PHPCompatibilityWP, pinning versions with --version= flag) and the logic for splitting a PHP-plus-plugins job into two deploys reflects genuine operational judgment, not textbook advice. The experience section is dense with specifics: 40 named-vertical sites, a measurable ticket-reduction (9→1 in one quarter), a named LCP improvement (4.3s→2.0s), and the update checklist authorship — none of these were supplied by any question. The one real concern is the absence of any evidence of unit or integration testing (PHPUnit, Jest, or even manual test scripts beyond manual cart checks), which the role brief calls out; the candidate's confirmation method is thorough manual QA but not automated tests, and that gap should be probed at interview. At $1,400 against a $1,500–2,800 band this is an easy hire on compensation; present to client pending a 30-minute live debug exercise and a direct question on…

Experience

6 years maintaining WordPress sites, the last 4 at a Manila web agency that runs monthly care plans for about 40 US small-business sites — law firms, dental clinics, a home-services company and three WooCommerce stores. I ran the update cycle for all of them, staging first, and wrote the agency's update checklist; emergency tickets caused by updates went from 9 in the quarter before it to 1 in the quarter after. I also fix slow sites: took one WooCommerce store's mobile LCP from 4.3s to 2.0s.

Scenario answer

Before the Update button, in this order. 1. A backup I have actually restored. The host's snapshot, plus an off-site copy with UpdraftPlus and a wp db export. Then I restore it into LocalWP. A backup nobody has restored is a hope, and old sites are where you find out. 2. An inventory, written down. wp plugin list --update=available gives me the installed and new version of each plugin, Site Health gives the PHP version, and I run wp core verify-checksums and wp plugin verify-checksums --all. On an abandoned site someone has usually 'quickly fixed' a plugin file, and the update silently deletes that fix. 3. Read the code the updates will touch. Is it a child theme, or was the parent edited directly? If edited, I diff it against a clean copy of the same version first. Then I search the theme and any custom plugin for calls into the six plugins — hooks, shortcodes, template overrides — because that is what breaks when a plugin changes. 4. Changelogs for every version in the gap, not just the latest: a new minimum PHP or WordPress version, a database migration, anything removed or renamed. 5. A staging clone on the same PHP version as live — WP Engine or Kinsta staging, or LocalWP — with WP_DEBUG_LOG on and display off. Then on staging, one at a time: plugins that still support the current core, then core, then the remaining plugins, then the theme. After each, I test the pages that make the client money — home, contact form (and that the email arrives), checkout, login — and read debug.log. Slower, but when something breaks I know which update did it. If the first check finds a conflict — say the booking plugin's new version needs PHP 8.1 and the server runs 7.4 — I split it into two jobs. Everything that works on 7.4 goes live first. Then PHP on its own: staging to 8.1, PHPCompatibilityWP over the theme and custom code, fixes in the child theme, and only then that plugin. I never change PHP and six plugins in one step; if something breaks, you can't tell which change did it. Live: a fresh backup, a quiet hour, the same order, each plugin pinned to the version I tested with wp plugin update <slug> --version=<version>. Rollback is the host snapshot, and the client knows beforehand how long it takes. To the client: 'Yes, everything gets updated — this week, not all at once, and not on the live site first.' One limit: if a premium plugin's license has lapsed, I can't update it until they renew. On sites this old, that's common.

Work sample

SYMPTOM A WooCommerce store selling home-gym equipment, about 1,100 products. The host moved it from PHP 7.4 to 8.1 on a Tuesday. On Friday the owner forwarded a customer email: 'There has been a critical error on this website' on the cart page after entering a ZIP code. Only some carts. Small items checked out fine, which is why nobody noticed for three days. WHAT I FOUND I cloned the site to Kinsta staging on PHP 8.1, turned on WP_DEBUG_LOG and rebuilt a failing cart — a squat rack to a Colorado ZIP. debug.log had it on the first try: 'PHP Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given' in the child theme's inc/freight.php. A previous developer had written a freight surcharge in the child theme, hooked to woocommerce_package_rates. It read each product's freight-class field and called count() on it. 214 products had been imported without that field, so it came back null. PHP 7.4 only logged a warning, and WP_DEBUG was off, so nobody saw it for two years. PHP 8 throws a TypeError, which is fatal, so any cart holding one of those 214 products died at shipping. WHAT I CHANGED 1. In the child theme: replaced the count() with a check for a non-empty array, and made products with no freight class fall back to the oversized rate instead of silently getting no surcharge. That fallback changes what customers pay, so I asked the owner before choosing it. 2. Exported the 214 products and sent the list to the owner's team to fill in the field. 3. Ran PHPCompatibilityWP over the child theme and the two custom plugins to find the next one. It flagged four places passing null to string functions — only deprecation notices on 8.1, but I fixed them while I was there. HOW I CONFIRMED - On staging, 10 carts across several states, mixing the 214 products with normal ones: no fatals, and for products that had a freight class, shipping matched the old totals to the cent. - Deployed through Git, not the theme file editor, so the change has a history. - Left WP_DEBUG_LOG on in production, writing to a file outside the web root, and read it daily for a week. Zero fatal errors, and oversized-item orders were back the same day. WHAT I'D DO DIFFERENTLY The host had announced the PHP change two weeks earlier, to an address nobody read. Now every care-plan site's PHP end-of-life date is on my calendar, and each site is tested on the next PHP version a month before the host moves it.

Sipho D.

SCREEN 88/100

South Africa · asking $1,700

Screening notes

The work sample is the standout: candidate independently describes the exact Gutenberg block-validation mechanism (save-function diff against stored post_content, front-end serving raw HTML), supplies a real number (3,412 affected posts), a precise fix (deprecated entry rather than DB migration), a targeted Playwright test across 60 random posts in CI, and a named process change — none of which were supplied by the question, so these are genuine signals of experience. The scenario answer is methodical and tool-specific throughout: wp core/plugin verify-checksums, BackstopJS for visual regression, Health Check troubleshooting mode for session-isolated bisecting, Query Monitor for stack traces, and the forked-parent-theme judgment with written client disclosure is unusually mature. Experience section is dense with verifiable specifics (30,000 posts, 14 editors, 9 plugins, 23 blocks, LCP 3.8→1.7s, Laravel background) rather than adjectives. The only real gap against the brief is WooCommerce, which the candidate explicitly flags as weak; that is honest but relevant if the hiring client needs it. At $1,700 the candidate is at the very bottom of the advertised band despite being the most qualified WordPress profile a recruiter would likely see at this level; worth flagging to the client as potentially underbidding.

Experience

8 years as a PHP developer, the last 5 as the in-house WordPress developer for a US nonprofit news publisher — about 30,000 posts, 14 editors and a membership paywall — working remotely from Johannesburg. I own the custom theme, nine custom plugins and 23 Gutenberg blocks, all in Git with automated deploys to staging and live. I moved the archive from the classic editor to blocks without breaking old posts, and cut mobile LCP on article pages from 3.8s to 1.7s. Before WordPress I built Laravel apps for a Cape Town agency.

Scenario answer

Before anything is updated, I want the site reversible, readable and reproducible, in that order. Reversible: a full backup, database and wp-content, restored somewhere else and seen working, plus the host's snapshot as a second copy. Readable: I put wp-content into a Git repository exactly as it is today. That baseline is how I find out what people changed by hand, and on an 18-month-old site I check three places: core files (wp core verify-checksums), plugin files (wp plugin verify-checksums --all), and the parent theme, diffed against a clean copy of the same version. Then I read functions.php and any custom code for every call into the six plugins. A plugin update rarely breaks the plugin; it breaks the code that depends on it. Reproducible: a staging copy on the same PHP version, and before changing anything, screenshots of the 20 most-visited URLs. After each update I compare them with BackstopJS, so a broken layout on a page nobody thought to check still shows up. Then the changelogs for every version in the gap: minimum PHP and WordPress versions, database migrations, removed features. If the first check uncovers a conflict, it depends what kind: - A plugin's new version needs newer PHP: I hold that plugin back, update everything else, and treat the PHP upgrade as its own piece of work with its own test pass. - Two plugins that clash once updated: I bisect on staging with Health Check's troubleshooting mode, which disables plugins for my session only, and read the stack trace in Query Monitor. - The parent theme was edited directly. Here I part ways with the usual advice. A few template tweaks go into a child theme. But if someone rewrote a third of the parent, I stop treating it as a vendor theme: I fork it into the repository and rename it so WordPress never offers the vendor's update over the top. From then on we own it, including its security fixes, and I put that cost to the client in writing. The update itself happens on staging, one plugin per commit, with the visual comparison after each. Live gets the same commits through the deploy, then wp core update-db and any plugin database upgrades. Nobody clicks Update on production. The client gets a short note: what I'm updating, what I found, and the one thing I need from them — on sites this old, usually a lapsed premium licence.

Work sample

SYMPTOM On a Monday morning the publisher's editors started posting screenshots in Slack: older articles with our 'Key facts' box showed 'Block contains unexpected or invalid content.' in the editor. The live site looked fine. Two editors had clicked 'Attempt recovery' on some posts, and on a few of those the box lost its heading. WHAT I FOUND The cause was my own change. On the Friday I had shipped a heading-level option for the block, and in the same change the wrapper went from a div to an aside. A static block stores its HTML in the post. When the editor opens a post, it runs the block's current save function on the stored attributes and compares the result with the stored HTML; if they differ, the block is marked invalid. The front end was untouched because it serves the stored HTML and never runs save. I had tested the change on new posts only. A search of post_content for '<!-- wp:newsroom/key-facts' found 3,412 posts using the block. WHAT I CHANGED 1. Added a deprecated entry to the block's registration with the previous attributes and the previous save function. The editor now recognises the old markup as an earlier version and upgrades it when a post is opened. Nothing in the database had to be rewritten. 2. Restored the 9 posts where 'Attempt recovery' had dropped the heading, from revisions, one at a time, each checked by an editor. 3. Added a rule to the repo, enforced in code review: any change to a save function ships with its deprecation in the same pull request. HOW I CONFIRMED - On staging, a Playwright test opens 60 random posts from the 3,412 in the editor and fails if the invalid-content warning appears. It passed, and it now runs in CI on every block change. - Front end: rendered HTML of 20 posts before and after, compared with diff. Identical. - The editors confirmed the same afternoon, and there have been no reports since. WHAT I'D DO DIFFERENTLY Test a block change against existing content first. New posts are the one case where a changed save function always looks fine.

Paula E.

SCREEN 72/100

Colombia · asking $1,150

Screening notes

Strongest evidence is the work sample: candidate independently identified server-side SMTP failure on shared hosting, reached for WP Mail SMTP unprompted (tool appears in their toolkit, not planted by the question), added 'Collect Submissions' as a belt-and-suspenders measure, and closed the loop with client confirmation over multiple days — that is a real practitioner's workflow, not a textbook answer. The scenario answer shows correct instincts (backup first, one-at-a-time updates, staging preferred, night-window fallback, plugin-support escalation path) but lacks any specifics the question did not supply: no version numbers, no named plugin conflicts they have actually encountered, no mention of child themes or reading the code before touching it — both must-haves in the brief. Experience section names real tools and client verticals (restaurant, law, clothing store) which is better than adjectives, but four years with no numbers (sites maintained, average ticket size, uptime incidents) keeps it in the credible-but-thin zone. Biggest concern is depth on PHP and plugin-conflict debugging: the SMTP fix required zero PHP and the scenario answer never touches the code layer, so the must-have 'reads the code before changing it / PHP debugging' is undemonstrated; at $1,150 the rate is compelling but the role brief requires someone who can open functions.php without flinching, and that capability is not yet visible. Verdict: advance to a short technical screen focused on…

Experience

4 years building and maintaining WordPress websites for small businesses. I started at a digital marketing agency in Medellín, and for the last 2 years I have worked as a freelancer for clients in the US and Colombia — restaurants, a law office, an online clothing store and several service businesses. I build the sites with Elementor Pro and Astra, do the monthly maintenance and updates, and fix problems when the clients report them. I am very responsible and detail-oriented, and my clients trust me with their websites.

Scenario answer

I understand why the client wants to update everything, because an outdated site is a security risk. But updating everything at once after 18 months can break the website, so I would be careful. Before touching the Update button, the first thing I would do is a full backup with UpdraftPlus, including the database and the files, and I would download a copy to my computer. Then I would check each plugin's page on WordPress.org to see if the new version is compatible with the latest WordPress, using the 'Tested up to' information, and read the recent reviews and support forum to see if other users are reporting problems. I would update the plugins one by one and check the website after each update to make sure everything is working, and then update WordPress. If the hosting has a staging option I would do it there first, but many small business hosting plans do not have one, so I would do the updates at night when there is less traffic. If I found a conflict, I would deactivate the plugin that is causing the problem, restore the backup if necessary, and contact the plugin's support. If they cannot fix it, I would look for an alternative plugin that does the same thing. I always keep the client informed during the whole process.

Work sample

PROBLEM: The contact form stopped sending emails A US client with a cleaning company told me they had not received any messages from their website contact form for about two weeks, which was strange because they usually receive several every week. WHAT I CHECKED: First I tested the form myself. I got the success message but the email never arrived. I checked the form settings in Elementor and the recipient email was correct. It was not in the spam folder either. WHAT I FOUND: The hosting server was not sending the emails correctly, which is a common problem with shared hosting. WHAT I DID: I installed WP Mail SMTP and connected it to the client's Google Workspace account, so the emails are now sent through Gmail and not through the server. I also made sure 'Collect Submissions' is active on the form, so in the future the messages are saved in WordPress too. HOW I CONFIRMED: I sent a test email from WP Mail SMTP and it arrived. Then I submitted the contact form again and the client received it. I asked the client to tell me when the next messages from customers arrived, and a few days later they confirmed everything was working.

This is the artifact every search delivers

Post the role and the same screener runs on your real applicants — the questions arrive already written, and you read every finalist in full before any fee is due.

Post a role free Or have us run the search

The same screener, other roles

Sample shortlist: WordPress Developer | Rolemote