for ingredients, etc.). Also, add a filter on the_content to automatically append recipe data at the end of posts of type “Recipe” if user forgets the shortcode. Essentially, this plan describes exactly how and when output is injected on the front-end. If [surface] were something like “Site footer”, you might plan: Use the wp_footer action to inject HTML (with an option to disable in settings). Having this plan makes implementation straightforward and reveals any potential issues (like needing to conditionally add assets only on pages where shortcode appears, etc.).
• Create a Gutenberg block (if needed) — attributes, editor UI, render strategy
If your plugin benefits from a Gutenberg (block editor) block, you should create one for a better user experience in modern WordPress. Define the block’s attributes (data it will store), its editor appearance, and how it renders on the front-end. For example, a “Testimonial” block might have attributes: author name (string), photo (image URL/id), quote text (string). In the editor, you’d provide controls to fill these in (using React components like text inputs, media uploader, etc.). You decide if it’s a static block (content saved directly in post content) or a dynamic (server-side rendered) block where render_callback in PHP generates output on view  . Dynamic blocks are useful if content needs to be up-to-date (e.g. a block that lists latest posts). Plan the block’s JSON metadata (in block.json): its name, title, category, icon, attributes, and default content or styles  . Also, consider internationalization (all text labels in JS should be translatable via provided scripts). Block CSS/JS: decide if it needs custom styles (probably yes) and ensure to enqueue those for both editor and front. Use wp_enqueue_script/style in the editor_script/editor_style and style keys of block.json . The block’s editing experience should as closely as possible preview the front-end result (Gutenberg encourages “what you see is what you get”). Thus, if your block output has styling, include that in the editor_style so authors see it. Document any block-specific instructions (like if the block requires an API key to function, handle that gracefully by showing a placeholder prompt in editor). By creating a block, you integrate with the block editor’s user flow – which is a big plus for user experience. Just ensure you also gracefully degrade (if someone not using block editor, maybe provide a shortcode fallback or legacy widget).
• /wp-block:block-spec [block]
Define the specification for a new block [block]. For instance, if [block] = “FAQ Accordion Block”:
• Name/Slug: myplugin/faq-accordion (appears as core/mine in code).
• Title: “FAQ Accordion” (shown in block inserter).
• Category: e.g. “Widgets” or “Design” (where it appears in inserter).
• Icon: choose a dashicon or SVG (perhaps “editor-help” for FAQ).
• Attributes: e.g. faqItems (array of objects with question & answer), each having { "question": {"type": "string"}, "answer": {"type": "string"} }.
• Editor UI: In editor, it could use InnerBlocks (each question-answer pair as a nested block) or a custom sidebar control to add Q&A. Possibly simpler: a repeater field UI in block. Outline: user can click “Add FAQ” and gets two textareas for Q and A.
• Front-end Render: each Q&A will output as an accordion (e.g. ), and include a script to toggle answers. Could use a small inline JS or depend on an existing library.
• Styling: default styles to hide answers initially and reveal on click (for front-end). In editor, likely always show answers expanded for ease of editing.
• Dynamic or static: Possibly static – save the HTML so it works even if plugin deactivated (though then accordion behavior might break without JS). Or dynamic – output only container in post content and use render_callback to generate Q&A markup (ensuring up-to-date JS is loaded).
• Translation: Ensure question and answer labels in editor are translatable.
This spec covers everything from user interface to output for the block. It serves as blueprint so developers writing the block code know what to build and how it should function.
• /wp-block:attributes-schema [block]
Provide the JSON-like schema of attributes for the [block]. Suppose [block] = “Testimonials Slider”: attributes could be:
attributes: {
testimonials: {
type: 'array',
default: [],
source: 'query',
selector: '.testimonial',
query: {
name: { source: 'text', selector: '.testimonial-name' },
quote: { source: 'text', selector: '.testimonial-quote' },
imageUrl: { source: 'attribute', selector: '.testimonial-img img', attribute: 'src' }
}
},
autoplay: { type: 'boolean', default: false }
}
Explain each: testimonials is an array of testimonial items, each with name (text), quote (text), imageUrl (from an image tag’s src). autoplay is a boolean for whether the slider auto-advances. This schema tells Gutenberg how to serialize the block content to HTML and back. For dynamic blocks, the attributes might be simpler because rendering is done server-side (you might then rely on save() returning null and using PHP). But it’s still useful to outline for consistency. This plan ensures all needed data points are captured and have proper default types. By explicitly writing the attributes schema, you avoid runtime mistakes and can communicate to others (or in docs) what each attribute does.
• Handle assets correctly — enqueue, deps, versions, conditional loading
Front-end assets (JS, CSS, images) should be added using WordPress enqueue functions so they integrate with core and other plugins. Use wp_enqueue_script() and wp_enqueue_style() in appropriate hooks (e.g. wp_enqueue_scripts for front-end) . Set dependencies for your scripts/styles explicitly: e.g. if your script needs jQuery, include array('jquery') in the deps argument . Provide a version number (usually use your plugin version or a file hash) so browsers can bust cache on updates . Conditional loading: don’t load all assets on every page if not needed. If your plugin has a front-end widget that only appears via shortcode or on certain pages, you can check is_page() or better, only enqueue when the shortcode is present. One strategy: use wp_enqueue_scripts normally for assets needed globally (rare), but for content-specific assets, enqueue them within the shortcode callback or block rendering function (so it only adds if the shortcode/block is used). For admin, use admin_enqueue_scripts and check $hook to only load on your plugin’s admin pages  . Always namespace your handles (like 'myplugin-style') to avoid conflicts. If using inline scripts or style tags, minimize them – better to output needed data via wp_localize_script() for dynamic data to your JS . Also consider performance: aggregate or minify assets (you might have a build step that produces minified files for release). But if using WP’s built-in dependencies (like using core-bundled jQuery UI), leverage those instead of adding new files . Lastly, if your plugin outputs HTML that might be affected by theme CSS, ensure your CSS loads after theme (set proper dependency or in footer) and be specific enough to override only where needed. By handling assets thoughtfully, you reduce bloat and prevent breakage: users will appreciate that your plugin isn’t injecting tons of CSS/JS on pages where it’s not used.
• /wp-assets:enqueue-plan [scripts] [styles]
Plan out the enqueueing for [scripts] and [styles] in your plugin. For example, if [scripts] = “frontend slider JS, admin settings JS” and [styles] = “frontend slider CSS, admin CSS”, the plan could be:
• Frontend CSS: myplugin-slider.css – enqueue on pages where shortcode [slider] is present. Implementation: in the shortcode handler, call wp_enqueue_style('myplugin-slider', plugins_url('css/myplugin-slider.css', FILE ), array(), '1.0.0'). Alternatively, if using a block, enqueue via block’s style in block.json so it loads automatically when block is used.
• Frontend JS: myplugin-slider.js (depends on jQuery) – enqueue alongside the CSS in the same conditions. Use 'jquery' as a dependency , ensure it’s in footer ($in_footer=true). Possibly use wp_localize_script to pass in user options (like autoplay speed).
• Admin CSS: myplugin-admin.css – enqueue on our plugin settings pages only. Implementation: use add_action('admin_enqueue_scripts', function($hook){ if($hook == 'settings_page_myplugin'){ wp_enqueue_style('myplugin-admin', ...); } }).
• Admin JS: myplugin-admin.js – similarly, only on relevant admin pages, depend on wp-util if using WP JS utilities, etc. Version all as plugin version.
• Images: If the slider uses arrow images or such, ensure these are referenced correctly (perhaps via CSS or with plugins_url() so paths are correct).
• Other: No assets loaded on front-end when plugin features not used (good). Use core dashicons if possible instead of custom icons to avoid extra files.
Summarizing this in text ensures each asset has a purpose, a loading condition, and we’re not forgetting to deregister on uninstall or similar (though usually leaving registration is fine; main thing is conditional enqueue). This plan guides the actual coding in functions.php or main plugin file for asset management and helps avoid common pitfalls like loading stuff everywhere or missing an asset when needed.
⸻
Integrations (REST / AJAX / External services)
• Add REST API endpoints (if needed) — routes, permission callbacks, validation
If your plugin exposes data or operations that could benefit external clients (JavaScript apps, mobile apps, or other sites), consider adding custom REST API endpoints. Use register_rest_route() on the rest_api_init hook to create endpoints  . Define a namespace (e.g. 'myplugin/v1') and resource path (e.g. ‘/items’) . Provide an array of options including supported HTTP methods and callback function, and crucially a permission_callback to secure the endpoint . The permission callback should check user capabilities or auth status (for public data, it can return true; for protected actions, verify nonce or user cap). Implement input validation: you can define endpoint arguments with args in register_rest_route and include validate_callback or sanitize_callback for each, or manually inspect $GET/$POST in your callback (but using the REST infrastructure is cleaner). For example, if an endpoint expects an integer ID, ensure it’s numeric; if expecting certain values, validate accordingly. Use WP’s REST response objects (WP_REST_Response) for structured responses and proper status codes. If your plugin’s endpoints modify data, protect against CSRF by requiring an authenticated nonce or OAuth token (note: the permission_callback typically handles this by checking is_user_logged_in() or specific caps). Document these endpoints for developers. Also, consider performance and security implications: don’t expose sensitive data without auth, and don’t allow dangerous actions without capability checks. By leveraging the REST API, your plugin’s data can be accessed in a standard JSON format and you avoid reinventing an AJAX/JSON system from scratch. Test your endpoints via /wp-json/your-namespace/route once implemented to verify outputs and permission denials are as expected (you should get 401/403 for unauthorized calls)  .
• /wp-rest:endpoint-spec [routes]
Outline the specification for REST routes [routes] your plugin will add. For example, if [routes] includes “GET /contacts” and “POST /contacts”, spec could be:
• GET /myplugin/v1/contacts: Retrieves a list of contacts. Auth: requires current user to have list_contacts capability (permission_callback checks this). Args: support per_page (int, default 10, validate as positive int) and page (int, default 1). Response: JSON with an array of contacts (id, name, email) and pagination headers (X-WP-Total).
• POST /myplugin/v1/contacts: Creates a new contact entry. Auth: user must have edit_contacts. Args: expects name (string, required), email (string, required, validate as email). Process: on success, returns 201 Created with the new contact data; on error (validation fail), returns WP_Error with 400 status.
• (If needed, detail DELETE or others similarly.)
Include what underlying functions these call (e.g. MyPlugin_Contacts::create()), and note any special cases (maybe “if email exists, return error 409”). This spec ensures implementation of the REST endpoints is consistent and covers security (caps) and data integrity (validation). It will also serve as documentation for API consumers.
• Implement AJAX actions (if needed) — admin-ajax, nonces, caps
For classic themes or quick admin requests not suitable for REST, you can use admin-ajax.php. Define your action endpoints by hooking into wp_ajax {action} (for logged-in) and/or wp_ajax_nopriv {action} (for guests) . For each AJAX action, do a capability check at the top of your handler function (e.g. current_user_can('manage_options') if appropriate) and a check_ajax_referer( 'myplugin_nonce' ) to validate the nonce . Nonces for AJAX are usually passed via wp_localize_script() as discussed, or by adding a hidden field in a form. Ensure your JavaScript calls the AJAX URL admin_url('admin-ajax.php') with the correct action parameter and nonce. Structure your handler to output a JSON response (you can use wp_send_json_success() and wp_send_json_error() which also call wp_die() for you) . For example, if the AJAX action is “myplugin_do_task”, in JS you do: jQuery.post(ajaxurl, { action: 'myplugin_do_task', security: myplugin.securityNonce, data: value }). In PHP:
add_action('wp_ajax_myplugin_do_task', 'myplugin_do_task_callback');
function myplugin_do_task_callback(){
check_ajax_referer('myplugin_nonce');
if( ! current_user_can('edit_posts') ){ wp_send_json_error('Not allowed', 403); }
// Process the task...
if($success){ wp_send_json_success(array('message'=>'Task done')); }
else{ wp_send_json_error('Error occurred'); }
}
Always wp_die() or use the wp_send_json functions to terminate, otherwise WordPress will output a 0. Using the proper hooks and nonce check protects against unauthorized use of admin-ajax (which could be a vector for CSRF if not protected). Also, remember admin-ajax runs with full WP loaded, so for heavy tasks consider optimizing or offloading to background processing. Keep your AJAX output lean (no extra HTML unless needed). This approach will allow your plugin’s JS to communicate securely with WordPress for dynamic features (like form submissions, background processing triggered by users, etc.) without reloading the page.
• /wp-ajax:action-spec [actions]
Describe the AJAX actions [actions] your plugin will support. For example, if [actions] = “save_settings, fetch_report”:
• Action: myplugin_save_settings (triggered from settings page via AJAX when “Save via AJAX” button is clicked). Auth: only users with manage_options. Nonce: myplugin_admin_nonce. Request Data: an array of settings fields (validated in PHP similar to normal form save). Response: JSON success with a message or the updated settings.
• Action: myplugin_fetch_report (maybe triggered to load a report via AJAX on the front-end). Auth: if this is front-end for logged-in users, perhaps requires nonce but no cap if all visitors can do it – still use nonce to prevent automated abuse. Request: maybe a report ID or date range. Response: JSON containing the report data (or HTML fragment to insert). Use wp_send_json_success({data}).
For each action, note if wp_ajax_nopriv_ is needed (i.e. if non-logged-in should access – e.g. a public form submission). Also note any rate limiting or special security considerations (like maybe these actions call external APIs, so treat carefully). With this spec, the implementer knows to create e.g. two functions myplugin_save_settings_callback and myplugin_fetch_report_callback, include appropriate add_action hooks for each, and how to structure the JavaScript to call them. It also ensures no AJAX entry point is left unsecured or undocumented.
• Commercial licensing (if premium) — keys, update server, graceful expiry UX
If your plugin uses a licensing system for premium features, design it in a user-friendly and secure way. License Keys: Provide a settings field for users to enter their license/API key. Validate the key format (e.g. certain length/characters) client-side lightly and definitely server-side (perhaps by an API call to your license server). Activation: When a key is entered, possibly perform an activation call (to your server) to register the site, and store an activation status locally (option meta). Use https for any such calls. Update Server: Premium plugins can still use WordPress auto-update mechanism by hooking into pre_set_site_transient_update_plugins to add your plugin’s update data if license is valid. Or simpler, use an established solution (like EDD Software Licensing or Freemius) which provides an update endpoint. Plan how often the plugin checks for updates (maybe leveraging the cron to avoid hitting your server too often). Graceful expiry: If a license expires or is invalid, do not break the site. The plugin should ideally continue functioning in core areas but might disable updates or certain premium features. For example, some plugins just stop delivering updates/support while allowing existing features to continue – decide your approach. In the UI, show the license status: e.g. “License active (expires 2024-01-01)” or “License invalid or expired – please renew to continue receiving updates.” Provide links to renewal or troubleshooting if the key fails. Ensure that even without a valid license, the free aspects still work (don’t white-screen or anything). Also consider an offline mode: if your license server is down, the plugin should fail open (assume license okay for some time) to not penalize users for connectivity issues. Security: protect any license-related endpoints with nonces and capability checks (likely only admins manage licenses). Data privacy: be transparent that the plugin contacts an external server for license verification – possibly mention this in readme or settings (“This plugin will connect to example.com to validate your license”). Overall, aim for the licensing to be as unobtrusive as possible for legit users while still protecting your business.
• /wp-pro:licensing-flow [model]
Outline the licensing flow for your plugin’s [model]. For example, if [model] = “Freemium with Pro upgrades”:
• Free Version (.org): no license needed. Contains code to recognize when Pro is installed/activated (perhaps a separate plugin or add-on) and hides redundant settings. Maybe provides a “Go Pro” call to action in the settings with link to purchase.
• Pro Version: distributed outside .org, requires license activation. Flow: After installing Pro, user sees a notice “Please activate your license to enable updates.” The settings page shows a License section with an input. User enters key and clicks “Activate”. Plugin sends https://yoursite.com/license-api/activate with key and site URL. If response OK (active), store license status (option: license_key and license_status=valid along with expiry or plan info). Schedule a cron to periodically verify or ping for updates. If activation fails (key invalid or max installs), show error message.
• Update Checks: The Pro plugin hooks into update checks; when WP checks for plugin updates, plugin adds its info if license_status is valid (or even if expired, could still notify but not allow download). Update package URLs are protected (maybe require key as param or a token).
• Expiration: If license expires, plugin should inform the user in WP Admin (non-intrusive banner “Your PluginName license expired on 2023-12-31. Renew for continued support and updates.”). Possibly continue functioning but disable update retrieval. The features ideally keep working – unless your model is subscription-based functionality (then you might disable the feature but again, do so gracefully and communicate it).
• Deactivation: Provide a way to deactivate license on a site (in case the user is moving it). Simply a button “Deactivate License” that calls your API to free the activation slot.
This flow covers end-to-end user experience for licensing. It ensures your implementation to be consistent and user-friendly, handling edge cases like server down or license misuse. Having it written helps when coding the license manager module and also for support docs.
⸻
Security / Privacy / Compliance for WordPress
• Enable debugging (dev only) — safe debug setup without leaking in prod
As a developer, you’ll use WP_DEBUG and other debug tools during development – but ensure none of that debug info shows on live sites. Encourage developers or site admins to turn on WP_DEBUG in staging or set define('WP_DEBUG_LOG', true) to collect logs  . In your plugin, you can add if ( defined('WP_DEBUG') && WP_DEBUG ) { … } around debug-specific code (like verbose logging or developer UI panels) so it doesn’t execute in production where WP_DEBUG is false . Provide a config or constant to enable plugin-specific debug mode (for example, define(‘MYPLUGIN_DEBUG’, true) to log extra info) – but default it off. Ensure any debug messages you log do not expose private data or sensitive info (if you log user input or API responses, strip or anonymize personal details). If using error_log, prefix messages with your plugin name to identify them. If you have a custom debug page in the plugin, hide it behind an administrator check or only load it when WP_DEBUG is true, to avoid performance overhead and information disclosure. On production, any caught exceptions or errors should fail gracefully – perhaps show a generic error message to users and log the details to debug.log for the admin. Never output raw PHP errors to users in production. Use @ini_set('display_errors',0) as WP does by default when WP_DEBUG is false . Essentially, test your plugin with debugging off to see that it behaves cleanly (no stray var_dump or warnings). And test with debug on to ensure it provides useful info for you without crashing. Having a safe debug setup means you and advanced users can troubleshoot issues without compromising the site’s stability or security.
• /wp-sec:debug-safety [env]
Describe how to maintain debug safety in [env] (likely “production environment”). For instance: In production, set WP_DEBUG to false and WP_DEBUG_DISPLAY to false so that PHP warnings/notices are not shown to end-users . Our plugin honors these settings and will not output any debug information unless WP_DEBUG is true. For extra safety, our plugin checks wp_get_environment_type(): if it returns ‘production’, we explicitly disable certain debug behaviors (like an on-screen SQL log or developer menu). We ensure any temporary debug code (like writing to a file) is conditioned on a constant that defaults off. We also avoid using var_dump/print_r directly; instead, use error_log which goes to a secure log when enabled. In summary, on a live production site, the plugin will run in quiet mode – any internal errors will be logged to debug.log (if enabled) or suppressed, and users will not see raw error messages. This statement can be part of developer documentation or comments, and it guides maintaining a strict separation between dev verbosity and production silence, which is a professional approach to WP development.
• Security hardening — nonces, escaping, sanitization, prepared queries, uploads
Develop your plugin with a security-first mindset in line with WordPress standards: Nonces to protect forms and actions (prevents CSRF). Whenever you create a form or link that performs an update or sensitive action, use wp_create_nonce() and verify it on processing (check_admin_referer() or check_ajax_referer() for AJAX) . This ensures the request is intentional and from a valid user session. Escaping output: any data coming from the database or user input that is output in HTML needs escaping to prevent XSS. Use functions like esc_html(), esc_attr(), esc_url() at the point of output (remember “escape late” principle)  . For example, when echoing a setting value in an input field, do echo esc_attr( $value ). Sanitization & Validation of input: before saving any user-provided data (from a form, API, etc.), clean it. Use sanitize_text_field() for plain text , sanitize_email() for emails, sanitize_file_name() for filenames, etc. . If expecting a number, cast to int; if expecting a specific format, use regex or WP provided validation. Also enforce capabilities – e.g. even if a nonce is valid, double-check current_user_can() for critical operations (a non-admin could potentially trick an admin into triggering something, but capability checks add a layer of safety). Database queries: use $wpdb->prepare() for any direct SQL with user input to avoid SQL injection. Eg: $wpdb->get_results( $wpdb->prepare("SELECT * FROM {$wpdb->prefix}mytable WHERE status=%s", $status) );. Or use the ORM-like WP functions (like WP_Query with args) which handle preparation internally. File uploads: if your plugin handles file uploads, use wp_handle_upload() to let WordPress manage it and check file types. Limit allowed file types to only what’s necessary (perhaps use wp_check_filetype() to validate extension/MIME). Save uploads in wp-content/uploads/... not an untrusted custom folder unless needed, and consider scanning or at least not executing uploaded files (e.g. if you allow SVGs, sanitize them; if you allow images, no extra actions needed since they’re not executed). Also, apply principle of least privilege: if your plugin uses external APIs, secure the API keys (don’t expose in front-end), and make remote requests safe (use wp_safe_remote_get() which checks SSL). If caching data, be mindful if any sensitive info is cached in public locations. Essentially, every time data crosses boundaries (user input to server, server data to output), apply the appropriate measure: validate or sanitize on input , escape on output . By following these practices, your plugin will align with WordPress’s robust security approach and protect both site owners and their visitors from common vulnerabilities.
• /wp-sec:hardening-checklist [feature]
Create a hardening checklist for [feature] of the plugin. For example, if [feature] is “CSV import/export”:
• Nonce: Confirm that the import form submission has a hidden nonce field, and the processing code calls check_admin_referer('myplugin_import_nonce').
• Capability: Ensure only users with appropriate capability (maybe manage_options) can access the import/export page and perform the action.
• File Handling: When importing CSV, use WordPress functions to handle the upload (wp_handle_upload). Only allow .csv extension, and perhaps check MIME text/csv. If parsing CSV, guard against extremely large files (maybe set a file size limit) to prevent denial of service.
• Sanitize Data: Each field read from CSV should be sanitized before insertion. E.g., if CSV has username and email, run sanitize_user() and sanitize_email() respectively on each. Don’t eval or directly execute any content from CSV.
• Escaping: For export, when generating CSV output, ensure that any data included (which might contain commas, quotes, newlines) is properly escaped or enclosed in quotes per CSV format to prevent breaking the structure.
• Database: Use prepared statements or WP functions when inserting imported data into database. E.g., use $wpdb->insert() which prepares internally, or sanitize then use in a prepare().
• Feedback: After import, do not display raw imported data in an HTML success message (to avoid XSS if CSV contained malicious content). Instead, just say “X records imported successfully.” Log details to a debug log if needed.
• Privacy: If the CSV contains personal data, treat it carefully. Don’t store the file permanently unless necessary (and if so, perhaps delete it after import or on plugin uninstall).
This checklist can be applied during development and testing of the CSV import/export to ensure all security angles are covered. It’s basically a specific application of general security practices to a particular feature. Doing this for each major feature (like user input forms, data display, external API integration) would ensure comprehensive coverage.
• Privacy/GDPR alignment — stored data, consent, export/erase support if needed
In the era of privacy laws (GDPR, etc.), design your plugin to handle personal data responsibly. First, identify what personal data (if any) your plugin stores: e.g. user emails, names, IP addresses, etc. If your plugin collects new personal data (say, via a form), consider adding a privacy notice or requiring user consent for that collection. For example, if you have a newsletter signup checkbox, ensure there’s a statement like “Your email will be stored for newsletter purposes” and only store it if they opt-in (don’t pre-check without consent). Use WordPress’s built-in Personal Data Export/Eraser hooks if applicable  . That means: if your plugin stores personal info outside of the normal WordPress user or comment tables, integrate with the Privacy tools. For instance, use wp_privacy_register_exporter() to allow site admins to include your plugin’s data in a user’s export upon request, and wp_privacy_register_eraser() to erase personal data your plugin holds  . An example: a plugin that stores form submissions (with name, email) should, when asked to export data for a given email, retrieve any entries with that email and return them in a structured format for export. And for erasure, perhaps delete or anonymize those entries (maybe replace name with “Deleted” and email with “[redacted]”). If certain data needs to be retained for legitimate reasons, document that. Also, data minimization: don’t collect more info than needed for functionality. If your plugin sends data to an external service (like an analytics or a remote API), disclose this in your plugin readme or admin screen (“Note: This plugin connects to Example API to fetch stock photos, transmitting query terms and your site’s API key”). Obtain consent if required (some plugins have a setting “Allow usage tracking” which is opt-in). For cookies: if your plugin sets any cookies (e.g. for tracking or functionality), list them and ensure they are covered by the site’s cookie consent if needed. Provide guidance in your readme’s Privacy section (the Plugin Directory allows a Privacy section) about what your plugin does with data. By aligning with GDPR and similar, you not only avoid legal issues but also make it easier for site owners to use your plugin without compromising their compliance. WordPress core’s privacy API and guidelines are there to help – make use of them so that things like “Personal Data Export” just work with your plugin’s data as well  .
• /wp-sec:privacy-map [data-types]
Make a privacy data map for the [data-types] your plugin handles. For example, if [data-types] = “Customer inquiries (name, email, message), IP addresses (logged for security)”:
• Customer Inquiries: Collected via a contact form. Fields: name (personal), email (personal), message (could contain personal info). Stored in custom DB table wp_inquiries. Retention: kept indefinitely unless deleted by admin. Use: only for site owner to respond to inquiries. Consent: user must tick “I consent to having this site collect my info via this form” (and that consent is recorded, perhaps as a boolean in the DB or simply implied by submission). Export/Erase: The plugin registers an exporter so that if a user requests their data, any inquiries with their email are included (with fields Name, Email, Message, Date). For erasure requests, the plugin either deletes those inquiries or anonymizes the personal fields (e.g. “[Removed at user request]” in name/message, blank out email).
• IP addresses: Captured in a log table for security (e.g. failed login attempts if plugin had that feature). Considered personal data under GDPR. The plugin should mention that it logs IPs for security purposes. Possibly provide a setting to anonymize IPs (e.g. only store partial IP). Consent: not explicitly asked because it’s under legitimate interest (security), but documented in privacy policy that IPs are logged. Export/Erase: If a user requests export of their data identified by email or user ID, IP logs might not be easily tied to a specific user unless you also log user ID. If they are, include them. For erasure, you could delete or anonymize IP entries older than X days regularly (data minimization) or specifically remove those related to the user.
The privacy map clearly lays out each data type, why it’s collected, how long it’s kept, and how the plugin facilitates compliance. This is useful for admins writing their privacy policies and for your own development to implement necessary features. You could also use this to create a Privacy Policy suggestion (many plugins output an example text for site owners to include in their policy – WordPress has a mechanism to add “Privacy Policy content” via add_privacy_policy_content() with a boilerplate for your plugin ). Ensuring each data-type is accounted for will keep your plugin on the right side of privacy best practices and user trust.
⸻
QA / Compatibility (WordPress reality checks)
• Set up dev environment — local WP, clean site, sample content
Establish a reliable development and testing environment to build and verify your plugin. Typically, set up a local WordPress installation (using tools like Local, XAMPP/LAMP, Docker WP environment, etc.) with the same version of WordPress you target. Use a clean site with no other plugins initially to test your plugin in isolation. Create sample content relevant to your plugin – e.g. if your plugin deals with custom post types or shortcodes, add some posts/pages using those to simulate real usage. This might include dummy users, terms, comments, or whatever data your plugin touches. Having a known dataset makes it easier to predict outcomes and spot regressions. Also, maintain a baseline site (perhaps exporting the DB so you can reset as needed) so you can always test fresh installation scenarios. For front-end features, include a variety of theme data (maybe the TwentyTwenty theme for simplicity and a custom one if needed). This environment should also have debugging tools like Query Monitor or error logs enabled so you can catch warnings. Additionally, consider setting up multiple environments: one matching minimum supported versions (e.g. WP 5.x, PHP 7.4) and one on latest (WP 6.x, PHP 8.x) to catch compatibility issues early. Document instructions for setting up the dev environment (for team members or contributors) – e.g. “Clone repo, run npm install for build tools, then use WP-CLI to scaffold a test site.” By front-loading environment setup, you reduce the friction in testing every aspect of your plugin under controlled conditions. Regularly update this environment (update WP, etc.) to catch changes that might affect your plugin.
• /wp-qa:local-setup-checklist [stack]
Provide a checklist for setting up a local test environment [stack]. If [stack] = “LAMP on Windows” for example:
• Install a web server (Apache) + PHP (ensure version matches target, e.g. PHP 8.0) + MySQL (or MariaDB). XAMPP could be used for simplicity.
• Download WordPress latest (or specific version) and set it up at http://localhost/wordpress .
• Create a database and user for WordPress, update wp-config accordingly.
• In wp-config.php, enable WP_DEBUG and WP_DEBUG_LOG  so we capture errors in wp-content/debug.log.
• Install a basic theme like TwentyTwentyOne.
• [Optional] If using tools like Node for build, install Node.js LTS.
• Check out the plugin code into wp-content/plugins/yourplugin.
• Activate the plugin through wp-admin. Verify no errors upon activation.
• Seed some content: create a few posts, pages, maybe import a WP theme unit test data set for general content. If plugin uses specific data (like CPT), create those entries.
• Test sending emails (if plugin does) – might need MailHog or similar if local server can’t send emails.
• Verify pretty permalinks are enabled (if plugin uses custom rewrite rules).
• Document how to run plugin’s unit tests (e.g. “run phpunit in plugin folder” if configured).
This checklist ensures anyone can quickly spin up a dev/test environment that mirrors what you need. For different stacks (VVV, Docker, etc.), you could have a variant, but the core is to have a reproducible environment.
• Compatibility testing — WP/PHP matrix + popular themes/plugins + hosts
Once the plugin works in a clean environment, test it across a matrix of WordPress and PHP versions that you intend to support. For example, if you support WP 5.8+, test on 5.8, latest 6.x; and for PHP, test on your min (say 7.4) and a newer one (8.1/8.2) to catch deprecation issues. Tools like WP CLI or Docker can let you swap versions easily. Next, test alongside popular plugins especially those in similar domain or big ones like WooCommerce, Yoast SEO, etc., to ensure no conflicts or ensure integration if needed. For instance, if your plugin adds content to posts, check it with a page builder plugin active, or if it’s an e-commerce addon, obviously test with WooCommerce enabled. Also, test with a variety of themes: at least a default theme (which usually is plain and works), and a few popular ones (some themes heavily modify admin or front-end which could affect your plugin). If your plugin outputs UI, see if it blends in or any CSS conflicts arise in those themes. Check in multiple browsers for front-end output (Chrome, Firefox, Safari) especially if you use JS/CSS heavily. Additionally, try different hosting environments: if possible, test on a Linux vs Windows environment (path issues), different MySQL versions (or MariaDB), maybe an environment with older cURL or strict security (some hosts disable certain PHP functions). If not physically testing on hosts, at least keep those considerations – e.g., ensure file operations use WP Filesystem API for compatibility with different FS setups. Another angle: test with localization – switch WP site to another language to ensure your text domains and translations work (even if you only have the .pot with no translations, testing switching ensures no strings left unwrapped in __()). By performing a thorough compatibility test matrix, you can confidently declare “tested up to X” and that your plugin “plays well with others.” It also reduces support burden down the line. Document any known incompatibilities (if something doesn’t work with a certain plugin or environment and it’s not feasible to fix, at least note it).
• /wp-qa:compat-matrix [targets]
Define the compatibility matrix for [targets]. If [targets] = “WordPress 5.9–6.3, PHP 7.3–8.2, WooCommerce, Yoast”, outline testing:
• WordPress: Test plugin on 5.9 (oldest supported) and 6.3 (latest). Ensure all features function, and no deprecated notices in debug log.
• PHP: Test on PHP 7.3 (lowest supported – though note WP’s current min is 7.4, but if you claim 7.3, test it) and PHP 8.2 (highest, with error_reporting on to catch deprecations). Look at debug log for “deprecated” or “warning” messages (e.g. certain functions might be deprecated in PHP 8). Fix or suppress appropriately.
• WooCommerce: Install latest WooCommerce and activate. If plugin integrates, test integration (e.g. if your plugin adds a tab in product page, see if it appears). Even if unrelated, just browse around with both active to ensure no obvious JS errors or performance issues. Check the Woo pages to see that your plugin’s assets aren’t accidentally loading everywhere (unless needed).
• Yoast SEO: Similarly, have it active and ensure, for example, if your plugin adds meta boxes or custom post types, Yoast doesn’t break or vice versa. If your plugin adds content to posts, see if Yoast’s analysis picks it up or not (could be fine if not, but just note it).
• Other: list any specific known heavyweights or page builders (Elementor, etc.) if relevant.
Present this as a table perhaps in documentation or internally. Mark each combination as “Pass” or any issues found. This becomes both a plan and a record of what was verified. If issues found, address them or list them. This matrix will reassure users (and the plugin team) that you’ve considered compatibility thoroughly.
• Performance review — queries, hooks, caching/transients, avoid global slowdowns
Analyze your plugin for any potential performance bottlenecks. Review all custom database queries – are they using proper indexes or could they be heavy on large sites? Use the Query Monitor plugin to see query count and time on pages where your plugin is active. If you find a query running too often or without caching, consider using the Transients API or WP Object Cache. For example, if your plugin computes a costly statistic on every page load, cache that result in a transient for some minutes . Only query what you need: use SELECT specific_columns instead of SELECT * when possible. Check loops: if you have nested loops or complex computations, see if they scale with number of posts/users, etc., and test on a site with a lot of data (maybe generate dummy posts using WP-CLI to simulate scale). Make sure you’re not hooking into extremely high-frequency hooks (like every the_content) with heavy code unless necessary. If you do hook into something like init or wp_head, ensure the code is optimized (no unnecessary queries or disk writes on each page). Offload lengthy tasks to background (via cron or asynchronous processing) instead of doing during page request. Profile memory if your plugin handles large data (maybe using the WP debug tools). Another important point: avoid loading too much on every admin page – only load your heavy scripts or data when on your plugin’s specific admin screens (using admin_enqueue_scripts with conditional, as mentioned)  . Use pagination for listing data in admin to not overwhelm. If your plugin adds columns to admin tables, ensure using manage_posts_custom_column efficiently (no heavy query per row; maybe bulk preload data). Also consider front-end impact: if your plugin adds CSS/JS, minify it and combine if appropriate, and load only when needed. Check that you’re not doing things like starting a PHP session or something site-wide that could slow down or conflict with page caching. In summary, make your plugin as lazy as possible – do work only when needed and as little as needed – and utilize WordPress caching APIs for repeated tasks  . Document any server requirements if your plugin is resource-intensive (like “requires at least 128MB PHP memory”). After making improvements, re-test with Query Monitor or similar to ensure lower impact. This performance vigilance will result in a smoother experience on both small and large sites, and often also correlates with better code quality.
• /wp-qa:perf-review [pages]
Conduct a performance review for [pages] affected by your plugin. For example, if [pages] = “Front-end post load, Admin listing page”:
• Front-end Post Page (where plugin maybe injects content): Load a sample post and measure queries and load time with plugin active vs. inactive. Check the number of queries added. Suppose you see it added 5 queries – examine them: if they are duplicate or can be combined, optimize. Use the transient cache if those queries don’t need to run every time (e.g. caching counts). Ensure no slow query (use EXPLAIN in SQL if needed to see if proper indices are used).
• Time the page generation using Query Monitor’s timeline – see if any particular hook or function of plugin is slow. If yes, profile that function (maybe using xdebug or manual microtime logging).
• Admin Listing (maybe a CPT list or settings page): If the plugin adds an admin menu listing posts or entries, make sure it paginates (e.g., shows 20 at a time). Measure the memory usage on that page – if loading 1000 entries at once, that’s a problem; adjust to using WP_List_Table with pagination. Check for any expensive operations when bulk actions are performed.
• Check asset sizes: front-end maybe +50KB CSS/JS from plugin – acceptable, but if it’s much larger, consider trimming or letting users dequeue parts if not used.
• Simulate multiple concurrent users (just a mental check or actual load test if possible) – ensure no obvious bottleneck like writing to a file on each page (which could lock).
After review, note down any changes: e.g. “Optimized query X by adding index on column Y in custom table” or “Added caching to function Z which reduced page load by 30ms.” The output of this review is both improvements done and any tips to users (“enabling object cache will benefit this plugin significantly” – you could mention if true). If you find that on large sites (like 10k posts) something is slow, consider it a bug to fix now, not later. This systematic review will ensure your plugin doesn’t cause a site to slow down and will uphold WordPress’s standard of performance.
• Automated tests (as possible) — WP test suite + smoke tests for key flows
It’s highly beneficial to include at least some automated tests for your plugin, even if just basic ones, to catch regressions. Set up the WordPress PHPUnit test suite for plugins (WP-CLI can scaffold it as mentioned, giving you a bootstrap that loads WordPress testing environment) . Write tests for critical functions: e.g. if you have a function that calculates a value or sanitizes input, write unit tests for various inputs and expected outputs. Use the WP test framework to create dummy posts/users etc. (for instance, use wpInsertPost in tests to simulate content and then run your plugin’s code). Also test integration points: e.g. if you register a shortcode, write a test that do_shortcode(’[yourshortcode]’) returns expected HTML. Smoke tests: These are basic end-to-end tests verifying that major features don’t break. For example, you might simulate an admin saving settings and then verify the option is stored. Or simulate a front-end form submission (perhaps by calling the PHP function that handles it) and checking the result. WordPress testing library allows calling AJAX handlers too (though a bit advanced). If GUI tests are desired, consider using Cypress or Playwright for a few scenarios (like load WP admin, click plugin menu, fill form, submit, verify result) – though that’s more involved. At minimum, aim to run PHPUnit tests on multiple PHP versions (this can be done via GitHub Actions for open source easily, running tests on 7.4, 8.0, 8.1 etc.). Having automated tests will greatly help when making future changes – you’ll know quickly if something fundamental broke. Additionally, run the WordPress Code Standards checks (PHP_CodeSniffer with WP rules) as part of CI, so your code stays consistent and catches things like unsanitized output. Even if you can’t test UI manually in CI, a set of functional tests that call your core classes and functions is very useful. Focus on the key flows: installation (maybe test that activation sets default options), data CRUD (create/read/update of plugin’s data), and any calculations. Over time, expand the test coverage. This practice not only increases reliability but also communicates to users/devs that the plugin is professionally maintained.
• /wp-qa:test-plan [critical-flows]
Develop a test plan for [critical-flows] in your plugin. If [critical-flows] = “User registration form submission, Data export feature”:
• User Registration Form (assuming your plugin adds extra fields to WP registration): Test Cases:
1. Submit form with valid data -> expect user created, extra fields saved (verify in database that meta is correct).
2. Submit form with missing required field -> expect form returns error message and no user created.
3. Submit form with invalid email -> expect validation error “invalid email”.
4. Security: try submitting with a bad nonce or as an unauthenticated context (if supposed to be authenticated) -> expect rejection (maybe 403 or redirect to login).
Automate: Possibly use PHPUnit to simulate form processing function by calling it with sample $_POST data (WordPress testing allows setting globals) and checking outcomes. Or use a tool like Behat (there’s a WP Behat extension) to simulate a browser form submission.
• Data Export Feature: (maybe your plugin’s custom data export via Tools) Test Cases:
1. As admin, request export for a user that has plugin data -> expect generated file contains that data properly formatted (e.g. CSV columns match).
2. For user with no data -> expect export file maybe just headers or a message “no data” but operation completes gracefully.
3. Unauthorized user (say a subscriber tries to access export page) -> expect access denied.
4. Malicious input: (if export takes query parameters, try injection to ensure it’s handled, but likely not applicable if it’s just a button).
Automate: Could call the function that generates export and verify output contents. If writing to filesystem, check the file. Clean up after test (remove file).
This test plan enumerates things to verify manually or in automated tests. It ensures that the most critical flows (ones that users rely on and that could break or cause support issues if not working) are always checked before release. It also indicates any edge cases to test (invalid input, permission checks). In sum, it’s like a checklist that QA or developers run through to say “yes, these fundamental things still work in version X.Y”.
⸻
Release / Distribution (WordPress.org + packaging)
• Establish coding standards — WPCS + PHPCS + formatting rules
Consistency and quality in code are important for long-term maintenance and community contributions. Adopt the WordPress Coding Standards for PHP (and JS/CSS to extent). This means things like: indentation with tabs, Yoda conditions, proper spacing, naming conventions (snake_case for functions, camelCase for variables in JS, etc.), escaping and sanitization practices as earlier. You can set up PHPCS (PHP Code Sniffer) with the WordPress Coding Standards ruleset to automatically flag deviations. Many IDEs can auto-format a lot of this or show warnings. Run PHPCS on your code periodically (or integrate into your build/CI pipeline)  to catch issues early. Also decide on other format preferences: for example, if using Prettier or eslint for JS formatting – align them to WP’s styles (which generally mirror core’s ESLint config). Document any exceptions or additional rules (maybe you want 120 char line length instead of 80, etc.). Beyond formatting, coding standards also include documentation: ensure every function, class, file has appropriate PHPDoc blocks (explaining parameters, return types, tags like @since, @param, @return). This not only helps others reading the code but also the Plugin Directory scanner might flag if no header docs on functions – possibly not, but it’s good practice. A consistent style makes the code base easier to read and reduces likelihood of errors (like missing text domain on translations or output not escaped because PHPCS can warn about that). Encourage using tools like Composer with WPCS or a package like WP Dev Lib to facilitate this setup. When contributions come in, run them through the standards check. It may seem pedantic, but having a clean codebase is also a reflection of plugin quality. The Plugin Team often recommends using WPCS to catch things before submission – it can even catch some security issues (like direct database queries without prepare). In summary, pick the standard (WordPress’s) and stick to it; use automated linters to enforce where possible .
• /wp-release:standards-setup [repo]
Detail how to set up coding standard checks for [repo] (your plugin’s repository). For example: We will integrate WordPress Coding Standards in the development workflow. Steps:
1. Add phpcs.xml config file at repo root targeting the WordPress-Core and WordPress-Extra rulesets (and WordPress-Docs for documentation maybe). Exclude any third-party libraries directory.
2. Require the WordPress-Coding-Standards/wpcs via Composer as dev dependency, along with PHPCS.
3. Add an npm script or composer script like "lint": "phpcs --standard=phpcs.xml" to easily run checks.
4. Optionally, add ESLint with eslint-config-wordpress for JS and stylelint for CSS with WordPress rules.
5. In CI (like GitHub Actions), configure a job to run composer install && composer run lint on push or pull request, so any standard violations are caught automatically.
6. Document in CONTRIBUTING.md that new code must pass PHPCS checks.
Also mention formatting tools: maybe use EditorConfig to enforce basic indent settings, etc., included in repo. Summarize any key rules (like “please use Yoda conditions and escape all output, PHPCS will remind you”).
This setup ensures that the repo is ready to maintain code style – it’s a one-time effort that pays off with every contribution and release.
• Build & release pipeline — versioning, changelog, clean zip, build assets
Plan how you go from development to a released plugin package. Versioning: decide on semantic versioning (e.g. Major.Minor.Patch). Update the plugin’s main file header and readme “Stable tag” to this new version when releasing  . Changelog: maintain a changelog in readme.txt (under == Changelog == section) summarizing new features, fixes since last release. Also possibly maintain a CHANGELOG.md for more detailed internal record. Build assets: if you use SCSS, TypeScript, or other sources, compile/minify them for release (maybe via npm run build that outputs to a dist/ folder or directly updates files in assets/). Ensure the built files have correct version strings (some do wp_enqueue_script( 'handle', 'file.js', ..., PLUGIN_VER, true), so update PLUGIN_VER constant). Clean zip: before packaging, remove any dev-only files – e.g. you might exclude tests/, .github/, composer.json, etc., from the distribution. If using SVN for .org, you simply won’t add those to trunk. If distributing on your site, create a script that zips the plugin folder excluding those files (many use svn export or gulp-rimraf tasks to prepare a clean folder). Check that the final zip has everything needed: main plugin file, all PHP/asset files, languages (.pot file at least), readme.txt, license. No sensitive info or keys. Release process: if on .org, committing to SVN trunk and tagging the version (the readme Stable tag should match tag name) will publish it  . For Git-driven, maybe use GitHub Actions to deploy to SVN or simply do it manually carefully. Announcement: update the changelog which will appear on .org, maybe draft a blog post or tweet if significant. Possibly mark the release on GitHub with notes if open source there. Also ensure any version constants in code are updated (some plugins keep a define('PLUGIN_VERSION','1.2.3')). After release, test the upgrade process from previous version on a dummy site. By having a clear pipeline, you reduce mistakes like forgetting to include a file or mis-numbering version. Tools can help: some use grunt-wp-deploy or svn-ci scripts. But even a manual checklist is fine: bump version, build, test, zip, upload. Document this pipeline so future maintainers know how to cut a release properly.
• /wp-release:package-plan [version]
Outline the steps to prepare and publish version [version] of the plugin. Example for version 2.0.0:
1. Code Freeze: Merge all final changes intended for 2.0.0 into the main branch. Ensure PHPCS linter passes and tests are green.
2. Bump Version: Update plugin header to 2.0.0 . Update readme.txt Stable tag to 2.0.0 . Update any internal version constants. Update changelog section: under == Changelog == add “= 2.0.0 =” with bullet points of changes.
3. Build: Run npm run build to compile/minify JS/CSS. Confirm dist/ files are updated.
4. Package: Create a clean directory for release. Copy plugin files except exclude: .git, node_modules, tests, etc. (We use an export script or do it manually). Ensure readme.txt and all needed files are in place.
5. Smoke Test Package: Install this packaged folder on a fresh WP site to ensure it activates and basic features work (quick sanity test that no file is missing, etc.).
6. SVN Commit: In SVN trunk, replace with new files (or if using GitHub action, push to SVN trunk). Commit with message “Preparing for 2.0.0 release”. Then copy trunk to tags/2.0.0 and commit. (If manual, also update Stable tag in trunk readme if needed).
7. Wait for .org: Check the plugin page after a few minutes to ensure the new version shows and assets (banner, etc. if any changes) look right.
8. Announce: Optionally, create a GitHub release or tweet announcing 2.0.0 highlights.
9. Support: Monitor support forum for any bug reports related to the new release and be ready to issue a quick 2.0.1 if a hotfix is needed.
This package plan ensures nothing is overlooked (version bump in multiple places, including doing the tag). It’s essentially a release checklist specific to the version but can be generalized for any version. Great for consistency and avoiding the “Oops, I released but forgot to update Stable tag, now .org isn’t showing new version” scenario.
• Internationalization (i18n) — text domain + strings + .pot generation
Make your plugin translatable so it can reach a wider audience. Use a consistent text domain (matching the plugin slug) for all __() or _e() etc. calls . Wrap every user-facing string in these functions – including default settings or any admin notices, etc. (the only exceptions might be dynamically concatenated bits, where you’d use sprintf with placeholders inside __()). If your plugin is on .org, .org will handle translation delivery, but you need to ship a .pot file (portable object template). You can generate this using tools: e.g. WP-CLI has wp i18n make-pot . languages/yourplugin.pot --exclude=node_modules. Or Poedit can scan and generate one. Ensure the pot file contains all strings and is placed in /languages folder (or /lang). Also load the text domain in your plugin: call load_plugin_textdomain('your-text-domain', false, dirname(plugin_basename(FILE )).'/languages') on init or earlier . That allows WordPress to find translations. Verify by using a dummy translation (e.g. use a tool like Loco Translate or just manually modify a .mo file) to see if a sample string translates. Also double-check you didn’t accidentally make typos in text domain in any call – any mismatch means that string won’t get translated (PHPCS can catch inconsistent text domain usage). If your plugin outputs dates or times, use date_i18n() for localization. For any plural or gendered strings, use _n() for plural forms. The .pot generation should ideally be part of your release process (update it each release so new strings are available for translators). If the plugin is not on .org but elsewhere, you might need to ship .mo/.po for languages you provide. But on .org, you don’t include .mo – only the .pot (and maybe an initial .po for English). The community or your team can then translate via translate.wordpress.org. By ensuring full i18n support, you not only comply with plugin guidelines but also greatly expand usability. It’s much easier to do this during development than to retrofit. Check even trivial strings like “OK” or “Submit” are wrapped – those often get missed. After release, check the translation project on .org to ensure your strings show up (the presence of a fresh .pot triggers update there).
• /wp-release:i18n-check [domain]
Perform an i18n review before release focusing on [domain] (the text domain). For example, if [domain] = “myplugin”:
• Search the code for (, _e(, _n(, etc. and ensure every instance uses ‘myplugin’ as the domain . No core domains or other plugin domains should be present (common mistake is copying sample code that uses ‘plugin-example’). Use PHPCS rule or grep to find any mismatches.
• Check that dynamic strings are properly handled. E.g., if concatenating, ensure translators have full sentences. Instead of "Hello ".$name, have a placeholder: sprintf( ("Hello %s","myplugin"), $name).
• Look at any strings with placeholders and ensure context is clear or use translators comments (/* translators: ... */) for clarification if needed.
• Update the .pot file: run the command to extract strings. Open the .pot to spot-check a few entries that they look correct (no odd things like untranslated words or dev comments).
• Confirm load_plugin_textdomain is called early enough (ideally on plugins_loaded or init). If you have separate admin vs public, it’s fine to load in each or one central place.
• If you introduced new strings this release, ensure they are added in readme’s changelog (translators might want to know significant new strings count, optional).
• Validate the .pot using POEdit or msgfmt to ensure no syntax issues.
• If you have language packs you maintain (for example you provide some translations yourself), update those .po/.mo from the .pot, increase their version if needed. But usually rely on .org.
This i18n check ensures that when you release, translators can immediately start working on any new strings and that your plugin will display in users’ languages. It’s part of a professional release routine especially as your user base grows globally.
• Write WordPress.org assets — readme.txt, screenshots, banners, FAQs
To have a good presence on the Plugin Directory, prepare the necessary assets: readme.txt is crucial (it was addressed partially above regarding content like changelog and header). Ensure readme is up to date: good short description (<=150 chars)  , clear long description explaining what the plugin does and its features, maybe usage instructions. Include a Screenshots section with captions that explain each screenshot (the actual images named screenshot-1.png, screenshot-2.png, etc., to be uploaded via SVN). Good screenshots can dramatically help users understand your plugin UI/UX – take them on a clean test site (preferably using default theme to avoid too much custom styling), crop to relevant area, and maybe add highlights if needed. If possible, create an appealing banner image (1544x500 px) which shows either your plugin name or illustrative graphic – not required but makes your plugin page look more professional. For FAQs, think of common questions or edge cases and list them in Q/A format. E.g. “Q: Does this work with multisite? A: Yes, with limitations X.” or “Q: How do I get an API key? A: Sign up at example.com, then enter it in plugin settings.” That saves you support effort later. Also mention any known conflicts or requirements here. Tags: ensure relevant tags (5 max) in readme header so people can find your plugin in searches . Contributors: add any .org usernames who contributed code or translations. After writing/changing these, use the Readme Validator to check formatting  (a badly formatted readme can break your listing). The readme is essentially your marketing copy and documentation – spend time to make it clear, helpful, and free of spelling errors. For plugin updates, update the Tested up to, etc., in readme so users see it’s actively maintained . If you have a donate link or website, put that in header too. The combination of a good description, helpful screenshots, and maybe a banner can significantly increase user trust and adoption.
• /wp-org:readme-draft [slug]
Create a draft of the readme.txt for [slug] (the plugin slug). For example, [slug] = “awesome-plugin”:
=== Awesome Plugin ===
Contributors: yourwporgusername
Donate link: https://example.com/donate
Tags: custom widget, posts, design
Requires at least: 5.5
Tested up to: 6.3
Requires PHP: 7.2
Stable tag: 1.0.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Awesome Plugin provides a set of custom widgets and design tools to enhance your WordPress site easily.
== Description ==
Awesome Plugin lets you add beautiful custom widgets to your sidebar, including a profile card, recent posts with thumbnails, and a social links panel. It also adds some design customizations like custom background patterns for widgets. No coding required – everything is configurable via the Widgets screen.
Features:
Profile Card Widget: Display an avatar, bio, and social media links.
Recent Posts Widget: Show recent posts with featured image thumbnails.
Styled Categories: Optionally restyle your Categories widget with icons.
Widget Backgrounds: Choose from 5 background patterns for widget areas.
This plugin is ideal for bloggers who want to personalize their sidebar. It uses WordPress best practices – no extra scripts on pages where not needed, and minimal styling to blend with your theme.
== Installation ==
Upload the awesome-plugin folder to the /wp-content/plugins/ directory, or install via the Plugins menu in WordPress.
Activate the plugin through the 'Plugins' menu.
Go to Appearance -> Widgets. You will see new widgets: "Profile Card", "Awesome Recent Posts", etc.
Drag them into a sidebar and configure the options.
Save and view your site – the widgets should appear with the chosen styles.
== Frequently Asked Questions ==
= Can I use this plugin with any theme? =
Yes, Awesome Plugin is designed to work with any theme. It inherits your theme's fonts and basic styling. The background pattern feature might look better on themes with wider sidebars.
= How do I add my social links in the Profile Card? =
In the Profile Card widget settings, there are fields for Facebook, Twitter, Instagram URLs. Fill those in (or leave blank any you don't want to show). Ensure you include the full URL (including http:// or https://).
= Is this plugin GDPR compliant? =
The plugin does not collect or send any personal data. It simply displays content from your site. If you use it to display personal info (like your profile), that's content you added to your site intentionally.
== Screenshots ==
Profile Card Widget in action – Shows the profile card with avatar, name, bio, and social icons in the TwentyTwenty theme sidebar.
Recent Posts Widget – Displays recent posts with thumbnail images and titles.
Widget settings – Configuration options for the Profile Card widget in the admin.
Background pattern options – The patterns you can choose for widget backgrounds.
== Changelog ==
= 1.0.0 =
Initial release of Awesome Plugin with Profile Card, Recent Posts, Styled Categories, and Widget Backgrounds.
== Upgrade Notice ==
= 1.0.0 =
First version.
This draft covers the essential sections. It can be tweaked for style and length (make sure Description isn’t too long; break into paragraphs for readability). The FAQ addresses likely user questions. The short description at top is within 150 chars. Tags are chosen relevantly. Once this is polished, it will be ready to include with the plugin submission.
• WordPress.org submission — guideline readiness + review response prep
Before submitting your plugin to the .org repository, double-check it meets all Plugin Guidelines  . This includes: GPL-compatible license (you’ve set that in header, and any bundled libs are also GPL or compatible), no phoning home without consent (if you have tracking, ensure user opt-in), no branding violations (don’t include “wordpress” in plugin name or slug improperly), no obfuscated code, no security issues (you’ve done your security and privacy checks), and no spam/unauthorized links (don’t insert links on user site front without their knowledge, etc.). Ensure your readme and assets are complete as above. When you submit (via the Add Plugin page), you’ll need a slug – using your desired slug is ideal, but if it’s taken, be ready with an alternative. The plugin review team will manually review the code for a first submission or major changes. They may respond asking for changes. Be prepared to respond professionally: if they flag issues, fix them promptly and reply via email with how you addressed each point. Common things they might flag: missing escaping, using file_get_contents instead of WP HTTP API, including your own outdated jQuery, using create_function (deprecated), big issues with naming collisions, etc., or not sanitizing input. If you get no response for a while (they aim for within a couple weeks), be patient (don’t harass them; they are volunteers). Once approved, you’ll get an email and an SVN repo. Then you push your code and it goes live. Also be ready to handle support – after launch, users may post questions; being responsive there improves your plugin’s reputation. Internally, have a plan for how to handle bug reports or feature requests – maybe set up a GitHub issues page or use the support forum. For updates, note that any time you commit a new version, there’s no manual review unless something triggers it (like security issues reported). But major updates can be also pre-reviewed if you ask. So maintain guideline compliance with each update. Also consider hooking into the Plugin Asset system (the assets## Product / PM for WordPress
• Validate the plugin idea — confirm the WP-specific pain + why existing plugins fail
Before building anything, ensure there’s a real WordPress-specific pain point your plugin will solve. Research how people currently handle that problem and why existing plugins might not fully address it (missing features, poor UX, etc.). Validating demand can involve surveying your target audience and analyzing support forums or reviews of similar plugins. A great idea should fill a gap or do something significantly better than what’s out there. Using the WordPress.org repository for a free MVP can also help gauge interest and get early feedback (the plugin repo “is a great way to validate your plugin idea”).
• /wp-pm:problem-brief [audience] [pain]
Key Points: When crafting a problem brief, clearly articulate who the target audience is (e.g. “small business site owners using WooCommerce”) and the specific pain point they face in WordPress. Focus on the user’s perspective: what workflow is frustrating or inefficient? Why is it a problem in a WordPress context? For example, “Non-technical bloggers (audience) struggle to optimize images for speed (pain), because existing plugins are too complex.” A concise problem statement like this guides development to ensure your plugin provides a real solution. It’s also useful for marketing, as you can communicate the pain point and your solution clearly to potential users (keeping messaging focused on solving that pain rather than just building tech for tech’s sake).
• Conduct an alternatives scan — research competing plugins (/wp-pm:alt-scan [competing-plugins])
Before finalizing your plugin concept, perform a scan of existing competing plugins. Search the WordPress.org plugin repository (and other marketplaces) for keywords related to your idea. Install and try out the top-rated or most popular ones. Note their feature sets, pricing models, user reviews, and any common complaints or feature requests. This competitive analysis helps you identify how to differentiate your plugin. Perhaps you’ll focus on a niche use-case the others ignore, or offer a simpler UI. Document the strengths and weaknesses of each competitor. WordPress has tens of thousands of plugins, so demonstrating why yours is needed (what gap it fills) is crucial to justify its creation. This research will also inform your plugin’s positioning and the value proposition you emphasize.
• Pick the distribution model — WordPress.org vs freemium vs premium licensing
Decide early how you’ll distribute and monetize your plugin, as it affects development and marketing. WordPress.org (free) distribution gives you exposure to millions of users and is often used for community goodwill or as a lead generator for a premium upgrade. Freemium model means a free base plugin (on .org) plus paid add-ons or a pro version; this balances reach and revenue, but you must manage two codebases (ensure the free version doesn’t violate .org rules by upselling too aggressively). A premium-only model (selling via your own site or a marketplace) means you can monetize all users but have to attract them without .org’s exposure. Consider your audience: if they expect free solutions, a freemium approach might work best. Also, if you choose commercial licensing, plan for how you’ll handle license keys and updates (e.g. a secure API to deliver updates to paying customers). Each model has trade-offs in support burden and revenue, so choose one aligned with your business goals and be transparent about it in your documentation.
• /wp-pm:monetization-plan [model]
Key Points: For the chosen monetization model, outline a plan covering pricing, feature split, and infrastructure. If freemium, decide which features are free vs. paid (ensure the free plugin is useful on its own, to comply with .org guidelines about not crippling functionality). If premium, decide on one-time purchase vs. subscriptions, and how you’ll sell (your own site, a marketplace, etc.). Plan a license key system for premium users to enable updates—WordPress doesn’t do this for you outside .org, so you may integrate an update API or use a service. Also, consider how you’ll support users (paid customers might expect faster support SLA). Document this plan internally so that development can be structured (e.g. using hooks or modules that can be unlocked for premium). A solid monetization plan ensures your plugin is sustainable and users understand the value they get for free vs. paid.
• Consider .org vs pro version constraints (/wp-pm:org-vs-pro-constraints [feature])
If you adopt a dual free/pro plugin strategy, carefully delineate what each version includes. WordPress.org rules require that the free plugin must not serve just as a teaser; it should have real, usable functionality on its own. You might offer basic features for free and put advanced or “power-user” features in the Pro edition. For each major feature, decide if it’s essential for broad use (likely in the free version) or a value-add for niche cases (could be pro). Also consider technical constraints: the free plugin can include upgrade prompts, but they should be subtle and not hijack the admin interface (constant nags or locked screens violate .org guidelines). Perhaps implement gentle notices or a settings page section listing premium benefits, without affecting normal use of the free plugin. By clearly separating features, you avoid confusion and ensure users have a smooth experience on both versions.
• Name and namespace strategy — slug, prefix, text-domain, brand consistency
Pick a unique plugin name/slug and use a consistent prefix/namespace in your code to avoid conflicts. The plugin slug (machine name) should be short and unique (and will dictate your text domain and URL on .org). For example, if your plugin is “Easy Custom Posts,” a slug might be easy-custom-posts. All your functions, classes, option names, etc. should be prefixed with a unique identifier, often an acronym of your plugin name, to prevent collisions  . E.g. ecp_activate() or class ECP_Admin. WordPress.org now requires the text domain to exactly match the plugin’s slug for translations, so set Text Domain: easy-custom-posts in your header and use ('some string','easy-custom-posts') in code. Consistent branding (in function names, UI labels, and documentation) makes your plugin look professional and avoids confusion with others. Best practice: a prefix of at least 4-5 characters is recommended  (to reduce chance of overlapping with core or popular plugins). If your plugin interacts with others (like an add-on), avoid using their reserved prefixes (wp , Woo, etc.) to stay compatible .
• /wp-pm:slug-namespace [plugin-name]
Key Points: Provide your [plugin-name] with a proper slug and code namespace strategy. For instance, given plugin name “My Form Builder,” you might choose slug my-form-builder. Use that slug as the text domain and as the basis for all global identifiers. This means prefix all custom functions, hooks, database options, etc. with mfb or similar. In modern PHP, you can also use actual namespaces or autoloaded classes — e.g. namespace MyFormBuilder... — which effectively prevent naming collisions. The goal is to ensure your plugin’s code doesn’t conflict with any other theme or plugin in the WordPress ecosystem. WordPress hosts thousands of plugins, so pick a prefix that is not a common word . Consistency here also aids debugging: anyone reading the code or support logs can immediately identify which plugin a function or setting belongs to by its prefix.
• Define minimum supported WP & PHP versions — decide baseline vs. target hosting reality
Determine the oldest WordPress version and PHP version your plugin will support, and declare them in your plugin’s readme header (Requires at least: X.Y and Requires PHP: X.Y). This decision should balance using modern features with not excluding too many users. Check the WordPress.org Stats for usage of versions: for instance, as of 2026, a vast majority of sites run fairly recent WP core (WordPress generally encourages staying updated), and WordPress itself runs on PHP 7.2+ (with PHP 8+ recommended). It’s often reasonable to require at least WordPress 5.0+ (if you need the Block Editor or other new APIs) and PHP 7.4+ or 8.0+, given older versions are end-of-life. Setting a higher minimum PHP (like 7.4) helps you use newer language features safely. Just be sure to test on that lowest version. Document these requirements clearly; WordPress will prevent installation on sites not meeting the PHP requirement and will show it on your plugin listing. Also, consider the “target hosting reality” – e.g. many budget hosts now offer PHP 8+, but some laggards might still be on 7.x. If a significant chunk of your audience might be on older environments, you may choose to be more lenient initially. In any case, code defensively: if using functions introduced in newer WP/PHP, guard them or provide fallbacks when possible.
• /wp-pm:support-matrix [wp] [php]
Key Points: Create a support matrix listing which WordPress versions and PHP versions ([wp] core and [php] engine) you will support and test against. For example: “Supported WordPress: 5.2–6.3; Supported PHP: 7.4, 8.0, 8.1”. This matrix should be guided by both WordPress’s official recommendations and your user base. WordPress core’s minimum PHP is 7.2 (but it recommends 8.0 or higher), so supporting 7.4+ is a common choice in 2026. Likewise, decide if you’ll support older WP (some plugins maintain backward compat a few versions back). Listing this matrix helps in your README and documentation, and it informs your testing plans. It’s good practice to test your plugin with the lowest and highest versions in the matrix before release. If you drop support for older versions in an update, communicate that in the changelog (and perhaps enforce it via the “Requires at least” header). Keeping this matrix up-to-date ensures users know what to expect and reduces support tickets from unsupported setups.
• Write a WordPress-first spec — admin pages, settings, hooks, roles, behaviors
When detailing your plugin’s functional spec or PRD (Product Requirements Document), frame it in WordPress terms. This means thinking through how it will integrate into WP admin and workflow: Will it add an admin menu or sub-menu (and under which heading)? What settings pages or options will it provide (using Settings API or custom UI)? Identify any new custom post types or taxonomies if relevant, or custom database tables. Define what user roles/capabilities are required for various actions (for example, only Administrators can change plugin settings, etc.). Consider using existing WP hooks where possible to implement behaviors (e.g. hooking into the_content filter to modify output, or firing custom actions on certain events). Outline how the plugin will behave in typical WP flows – e.g. on plugin activation, on post save (if your plugin ties into content), on front-end page load, etc. Incorporating WordPress patterns from the start (like nonces for form actions, respecting WP Cron for scheduling, etc.) will make your plugin feel native and reduce surprises. Essentially, design with “the WordPress way” in mind: leverage core APIs and ensure your plugin plays nicely with core settings (like multisite, localization, the REST API if needed, etc.). A clear spec focused on WP context will guide development and result in a more robust integration.
• /wp-spec:plugin-prd [feature] [wp-context]
Key Points: For each major [feature] of your plugin, describe it in the [wp-context] of how it will work within WordPress. For example, if the feature is “scheduled content cleanup,” note that you’ll use WP-Cron to schedule it (instead of a custom cron job) . If a feature is “user submissions form,” mention using admin-post.php or REST API endpoints to handle the form, with proper nonces for security. Essentially, tie every feature to a WordPress concept: hooks, shortcodes, blocks, user roles, meta boxes, etc. This will ensure your feature list isn’t abstract but grounded in implementable WP terms. In the PRD, you might have entries like “Feature: Export data to CSV – Implementation: add an admin page under Tools menu, accessible to users with manage_options capability, include a nonce-protected form to trigger CSV generation, and use WP Filesystem API to stream the download.” This level of detail (what WP capability, which hook or API to use, etc.) makes the development tasks clear and aligns them with best practices from the start.
⸻
Plugin Engineering Core (PHP / WordPress APIs)
• Choose plugin architecture — structure, autoloading, responsibilities
Decide on an appropriate code architecture based on your plugin’s size and complexity. For a small, single-purpose plugin, a simple procedural approach (all logic in a single file or a couple of files with functions) might suffice. For larger plugins, lean towards an organized structure with classes or namespaces to encapsulate functionality. WordPress doesn’t enforce a specific pattern, but common choices include: a singleton main class, static loader classes, or a more modular MVC-like separation. Plan how you’ll autoload classes (you can use PSR-4 autoloading via Composer, or manually require_once files). Determine responsibilities: e.g., have a clear separation between admin-facing code and front-end code. Best practice: separate your code into folders like /admin and /public (and maybe /includes for shared logic), so you only load what’s needed in each context. Also consider using design patterns like Hooks Loader (a class that registers all actions/filters in one place) to keep things tidy. A thought-out architecture will make maintenance easier as the plugin grows and will prevent the “one giant file with spaghetti code” scenario.
• /wp-dev:architecture [type]
Key Points: Document the chosen architecture [type] in your developer notes. For example, “Object-Oriented, singleton main class” or “Modular classes with namespacing”. Clarify how different parts of the plugin interact. If using a loader or plugin factory, mention that. If adopting an established boilerplate (like the WP Plugin Boilerplate), note any deviations from it. Outline your directory structure and what goes where: e.g. “All custom post type definitions in includes/class-myplugin-cpt.php, all admin page handlers in admin/ directory, front-end display logic in public/ directory.” By specifying the architecture, new contributors or your future self can quickly grasp the project structure. This section might also cover performance considerations of the architecture: e.g., “Uses conditional loading: admin code is only loaded on is_admin() to avoid overhead on front-end.” A clear architecture plan ensures consistency as multiple developers work on the plugin.
• Create plugin scaffold — header, main file, loader, activation/deactivation
Set up the basic scaffolding of your plugin. This includes the main plugin file (with the header comment containing Plugin Name, Author, Version, etc. as required), and any initial include/require logic to load other files. Implement activation and deactivation hooks early for any setup/cleanup tasks . For example, use register_activation_hook(FILE , 'yourprefix_activate') to maybe set default options or create database tables, and register_deactivation_hook(FILE , 'yourprefix_deactivate') for cleanup like clearing scheduled cron events. Ensure these callbacks are defined and, if needed, perform capability checks or environment checks inside them. The scaffold should also include a basic file/folder structure as planned. If you prefer automated tools, you can use WP-CLI: running wp scaffold plugin will generate a starter plugin with sample files and even a PHPUnit test setup. This can speed up initial setup, giving you a standardized foundation (including a uninstall.php file if you choose to generate one). The goal is to have a functional “Hello World” plugin structure that activates without errors, to which you can then start adding real functionality.
• /wp-dev:scaffold [slug]
Key Points: When scaffolding the plugin identified by [slug], make sure to include all WordPress-required elements. The main plugin PHP file should have the proper header with at least Plugin Name and other fields (like Requires at least and Tested up to if applicable). If your plugin will need to run upgrade routines or has settings, consider adding an uninstall.php at this stage (for future data clean-up) or at least a placeholder for it. Set up the loader pattern if using one – e.g., a static function to include required files (only when needed, such as admin files on admin pages). Also, implement a safety check at the top of each PHP file to prevent direct access (commonly defined('ABSPATH') || exit; to ensure the file is not executed outside WordPress context). This is a basic security step for your scaffold. In summary, scaffolding means creating the skeletal plugin so that it can be activated and deactivated cleanly, laying the groundwork for all future code.
• Plan data storage — Options API vs meta vs CPT vs custom tables
Decide how you will store any persistent data your plugin needs. WordPress offers multiple mechanisms: the Options API (simple name–value pairs stored in the wp_options table), various meta (post meta, user meta, comment meta tied to existing objects), Custom Post Types (CPTs) (leveraging posts table to store structured data with fields via meta or taxonomies), or entirely custom tables in the database. Each has pros and cons. For small amounts of settings (configuration toggles, API keys, etc.), using the Options API is straightforward – e.g. get_option('myplugin_settings') . If your plugin manages entries that naturally align with content (like “FAQs” or “Recipes”), a CPT is convenient and gives you free admin UI and WP_Query support. Post meta is great for attaching extra info to posts/users, but can become slow if you need to query by that data extensively (since meta queries can be inefficient for large data sets). If you expect to store a lot of records or need complex queries (especially many-to-many relationships, or data not fitting WP’s post schema), a custom table might be justified – but remember to use the WPDB and prepare statements for security. As a rule: try to use native structures (options or CPT) unless performance or data complexity forces a custom table. Document this decision so it’s clear to others. For example, “Plugin will use a custom post type ‘faq’ to store FAQs, with meta for additional fields, because it allows using the WP editing interface and list table for entries. We considered custom tables but decided against due to wanting revision support and avoiding reinventing the wheel.” If custom tables are used, plan migration and cleanup on uninstall.
• /wp-dev:data-plan [entities]
Key Points: List out the data [entities] your plugin will handle (e.g. “form submissions”, “scheduled tasks”, “widget settings”) and decide for each where to persist it. For each entity, note the storage choice: Option (for global settings or small bits of data), Meta (attached to a WordPress object), Custom Post (if treating entries as content), or Custom DB table (for complex or heavy data). For example: “User preferences – stored as user meta; Plugin global option ‘myplugin_settings’ – stored in options table; Custom data ‘Deals’ – stored as a custom post type deal with custom fields for price.” Justify each choice briefly using WP best practices: e.g., “Using CPT for Deals gives us the benefit of WP’s built-in post UI and queries, and deals behave like content (can be published/drafted).” If any entity demands a custom table (e.g., a log of 10,000 transactions that would be inefficient as posts), mention that and outline how you’ll create and index that table. This planning ensures no part of your plugin’s data is left without a deliberate storage strategy, and it aligns your development with WordPress’s data handling strengths.
• Register settings properly — Settings API + sanitization callbacks
If your plugin has settings, use the Settings API to handle them rather than raw form handling. This provides a standardized way to register settings, sections, and fields, and ensures user inputs are sanitized before saving  . Steps include: call register_setting( $option_group, $option_name, $sanitize_callback ) to register each option or option array your plugin uses . Provide a sanitize callback that will receive and clean the input (e.g., sanitize_text_field for simple text, custom callbacks for more complex data) . Use add_settings_section and add_settings_field to generate your settings page fields in the admin, which ensures they are output with proper markup and tied to the registered setting. The Settings API not only helps with output consistency (your settings page will look like WordPress core screens ) but also automatically handles things like user capability checks and nonce generation for the form . By using it, you avoid writing repetitive validation code and you integrate with core’s expectation that plugin settings are under an option name and sanitized on save. Remember to also escape settings on output (when rendering in HTML) even if they are sanitized on save, as a defense in depth. In summary, do not just update options directly from $POST; funnel them through the Settings API pipeline for robust handling. This approach gives you free security benefits (like default nonce protection of the submission ) and aligns with user expectations for plugin settings pages.
• /wp-dev:settings-api [settings]
Key Points: For each plugin [settings] page or setting group, implement it via the Settings API. Identify the unique option names you’ll use (e.g. myplugin_options as an array or multiple option keys) and register them with register_setting. Include a validation/sanitization callback in the registration to clean user input. For instance, if you have a setting “welcome_message”, you might register it like: register_setting( 'myplugin_options_group', 'myplugin_welcome_message', 'sanitize_text_field' ); – this ensures whenever that setting is saved, WordPress will run sanitize_text_field on it . For more complex settings (like an array), you might write a custom function to validate each sub-value. Also, plan the user interface for settings: use add_settings_section for grouping fields and add_settings_field for each input, specifying which HTML callback renders it (perhaps using WordPress field HTML for consistency). This structured approach means your settings page form will automatically include the proper nonce and submit to options.php (WordPress core handler) which checks user capabilities (manage_options) and calls your sanitization callback  . Document in code comments or dev notes which settings exist and how they’re sanitized, for future maintainers. This not only improves security (by whitelisting expected input)  but also future-proofs your plugin if WordPress changes how settings are stored or handled, since you’re using the official API.
• Hook into actions/filters — implement features “the WordPress way”
WordPress is driven by its hooks system (actions and filters), and your plugin should leverage this to integrate seamlessly. Identify the core actions or filters that relate to your plugin’s functionality and attach your callbacks to them . For example, if your plugin needs to modify post content, use the the_content filter; if it should do something on user login, use the wp_login action, etc. Implementing features via hooks ensures you’re not hacking core and that your code runs at the appropriate time in WordPress’s execution flow . Also, create custom hooks in your plugin where appropriate to allow extensibility (e.g., do do_action('myplugin_after_something', $data) so other plugins or themes can extend your plugin’s behavior). This hook-oriented architecture is idiomatic to WordPress and improves compatibility. For each major functionality, decide if it can be triggered by an existing WordPress hook. For instance, instead of creating a custom cron runner, you might tie into init or a WP-Cron schedule. Document which hooks you’re using and why. Example: “Uses admin_notices action to display success messages after settings save” or “Filters widget_text to support shortcodes in our custom output”. By deeply integrating with WordPress’s hook system, your plugin will behave predictably alongside others (since everyone’s sharing the same event system), and you reduce the need for manual checks or timing issues – let WordPress call your code at the right moment.
• /wp-dev:hook-map [feature]
Key Points: Make a hook map that lists each plugin [feature] and the WordPress hooks it relies on or provides. For example: “Feature: Auto-publish custom post – Hook: save_post (priority 10) to intercept saves and publish under conditions” or “Feature: Custom SEO meta output – Hook: wp_head to output meta tags, and filter pre_get_document_title for the title”. By mapping features to hooks, you verify that you are indeed implementing things at the correct points in WP’s lifecycle. This map also reveals if you’re not using hooks where you should – if any feature is accomplished by direct modification of core behaviors, consider if a hook exists for it. Additionally, if your plugin introduces its own hooks (actions/filters that developers can use), list them here: e.g. “Action myplugin_after_form_submit – fires after form submission is processed, for others to extend.” Providing this in documentation (like in a developer section of your readme or a wiki) can attract other devs to integrate with your plugin. Ultimately, a hook map is both a design and debugging aid, ensuring a clear contract of when and how your plugin interacts with the WordPress ecosystem .
• Prevent conflicts — use unique prefixes, no globals, safe resource handles, compatibility habits
Plugin conflicts are often due to shared names or overriding things unexpectedly. Continue the practice of prefixing everything unique to your plugin (functions, classes, global variables, option names) with your plugin’s slug or identifier . Avoid introducing unnecessary globals; if you must use one (like a global instance of your main class), prefix it clearly ($myplugin_instance). When enqueuing scripts or styles, use unique handles (e.g., wp_enqueue_script('myplugin-slider', ...); not just 'slider') so you don’t clash with core or another plugin’s assets. The same goes for image filenames, CSS class names, or HTML IDs your plugin outputs – namespace them (e.g., .mfp-gallery rather than .gallery). Do not use reserved prefixes like wp or __ which are used by WordPress core . When adding admin menu items or custom roles/caps, ensure their slug/names are unique. Perform a quick conflict audit of your codebase: scan for any very common names or overly generic terms. For example, a function named update_database() is too generic – make it myplugin_update_database(). Use tools like PHPCS with the WordPress Coding Standards rules, which will catch some bad practices. Also, follow compatibility habits: check for existence of functions/classes before defining (use function_exists if there’s a chance another plugin declared the same function) , and wrap your functionality so it only runs in appropriate contexts (e.g., don’t output admin notices on front-end). By proactively preventing conflicts, you save users from white screens or odd bugs when multiple plugins are active together. Remember that with thousands of plugins in the ecosystem, collisions are a real risk – but a disciplined approach to namespacing and scoping will mitigate most issues  .
• /wp-dev:conflict-audit [codebase]
How to perform: Conduct a conflict audit on your plugin’s [codebase] by searching for any unprefixed or generic identifiers. For instance, scan all function definitions, class names, constants, and global variables for names that aren’t uniquely yours. If you find any, rename them with your prefix. Check asset handles in your wp_enqueue_script/style calls – ensure each handle is prefixed (e.g., search your code for wp_enqueue_script( and see what strings you pass as handle). Look at any external libraries you include: are you loading an outdated jQuery plugin that might conflict? If so, maybe use the one bundled with WP if available, or wrap it to avoid global leaks. Test your plugin alongside other popular plugins that do similar things or use the same libraries – do they both work together? For example, if two plugins include the same PHP library class with the same name, that’s a conflict – consider using PHP namespaces or include guards (class_exists) . Use WP_DEBUG and WP_DEBUG_LOG during testing to spot any “cannot redeclare function” or “class already defined” errors (which indicate conflicts). The conflict audit should also verify you’re not modifying any global WordPress variables or output buffers in a way that persists beyond your scope. Essentially, treat every piece of your plugin as if it lives in a sandbox – check that nothing “leaks” into the global WP environment unless intentionally. Document the audit results and changes: e.g., “Renamed function X to Y to avoid collision with plugin Z.” This step greatly reduces the chance of users encountering compatibility issues when your plugin is one among many on a site.
• Ensure multisite compatibility — handle per-site vs network-wide mode
If WordPress Multisite support is needed, design for it upfront. Determine if your plugin should behave on a per-site basis (likely default) or have any network-wide features when network-activated. At minimum, test activating your plugin on a Multisite network: if network-activated, your activation hook should run for each site or appropriately handle shared data. Use the is_multisite() check in your activation routine if needed, and note that register_activation_hook passes a $network_wide parameter to your function (you can use this to set up options on all sites in the network if required). For settings pages: if your plugin has a config, should it appear in each site’s admin, or only once in the Network Admin? You might choose to add menu pages via network_admin_menu for network-wide settings. Plan how data is stored in multisite: site options vs. network options (add_option vs add_site_option for network-wide data) . Consider using the Network: true header in your plugin file if it only makes sense network-wide (this forces network activation). Also account for multisite when cleaning up on uninstall: provide a way to remove data from all sites, perhaps by iterating with get_sites() if the plugin was network-active. Document any multisite constraints: e.g., “In Multisite, plugin settings are per site” or “Use the network admin to configure global plugin settings.” Ensuring this from the start will prevent surprises, such as duplicate tasks running on every site when they should run once globally, or data not being isolated per site. WordPress’s APIs (like get_option vs get_site_option) make it possible to adapt – you just need to incorporate them consciously based on the desired scope of your plugin’s features.
• /wp-dev:multisite-plan [mode]
Key Points: Clearly state whether your plugin runs in single-site mode, multisite network mode, or both ([mode]). If both, outline how it behaves in each. For example: “In multisite, when network-activated, the plugin will … (e.g., create a settings page in Network Admin only and use network options). When activated on individual sites, it functions independently on each site.” If the plugin is meant to be network-only, use Network: true in the header so WP prevents single-site activation. If it’s per-site only, you might leave network activation possible but simply operate per site. Note any differences: e.g., maybe an expensive cron job should only run on the primary site when in a network – you can check is_main_site() to enforce that. Your multisite plan should also cover data handling: if a user triggers a personal data export/erase (GDPR tools) and your plugin stores user data, ensure you hook into those for each site or network-wide as appropriate. Another consideration: User roles – in multisite, a network admin has super admin capabilities which you might need to check (like using is_super_admin() if needed). Summarize: “Multisite Plan: The plugin can be activated per site or network. On network activation, it will create one global settings page and use site options for each site’s data, aggregated if needed. On per-site activation, it behaves normally on that site only.” This clarity in planning ensures you don’t overlook edge cases (like someone network-activating and expecting a centralized config). Test your assumptions in a multisite sandbox. By having a multisite plan, you make your plugin friendlier to WordPress installations that run multiple sites, which is common for larger organizations or educational institutions.
• Uninstall cleanup — implement uninstall.php for safe data removal
Plan how your plugin will clean up after itself upon uninstall (when the user deletes the plugin permanently). If your plugin adds database entries (options, custom tables, meta data, etc.), consider removing them to avoid clutter, but make it optional or clearly documented if data loss could surprise the user. WordPress offers two main ways: an uninstall.php file or using register_uninstall_hook in your main file. Using uninstall.php is straightforward – WordPress will execute it if the plugin is deleted via the admin, as long as that file exists. In it, you should check if (!defined('WP_UNINSTALL_PLUGIN')) exit; at top to ensure it’s only run in the proper context. Then, delete your plugin’s options (use delete_option and delete_site_option for network options), custom user/meta data, and any custom tables (drop them carefully with $wpdb->query, as in the official example). Never attempt to remove things not created by your plugin. You might provide a setting “Remove all data on uninstall” for the user to opt-in to destructive cleanup, which is a user-friendly approach. Also, do not confuse deactivation with uninstall: deactivation is temporary (so you usually don’t delete data then ), whereas uninstall means the user is done with the plugin and likely wants all traces gone . Follow the principle: if it’s easy for the user to recreate or if keeping it poses risks, clean it up; if it’s user-generated content (like custom post entries), maybe leave it or at least warn. Document what your uninstall routine does. For example, “Uninstall: removes all plugin settings and custom DB tables, but leaves custom posts intact (since those might be user content).” Implementing a proper uninstall routine keeps the WordPress database tidy and signals that your plugin is well-behaved, as it doesn’t leave orphaned data .
• /wp-dev:uninstall-policy [data]
Key Points: Define your plugin’s uninstall policy for each type of [data] it creates. List out the data artifacts (options, custom tables, custom post types, user roles/caps, transients, etc.) and state whether each will be removed on uninstall. For example: “On uninstall, the plugin will delete its custom options (myplugin_options), transient cache (transient_myplugin_*), and custom database table wp_myplugin_logs. It will not delete custom post type entries or media uploads created by the plugin, to avoid deleting user content.” Implement this in uninstall.php or via register_uninstall_hook accordingly. If some data requires conditional removal (e.g., only remove user meta if a certain setting was enabled), code that logic. Also handle multisite: if network-activated, loop through blogs to delete per-blog options or meta as needed (but note, this can be intensive – WordPress’s guideline warns that looping all sites for deletion could be resource-heavy). Perhaps leave a note encouraging manual cleanup in extreme cases. By being explicit in your uninstall strategy, you ensure no surprises: users who expect a clean removal get it, and those who might want data preserved know what to expect. Including this in your documentation or even a prompt (“Do you want to remove all plugin data?”) can be good. Technically, your uninstall script should cover all data listed in this policy. This thoroughness is part of WordPress plugin best practices  and can help your plugin approval as the review team often checks for a proper uninstall routine (especially if your plugin creates tables or stores lots of data).
• Database migrations — schema versioning and upgrade routines
Plan for the possibility that your plugin’s data schema might change in future updates (new options, altered table structure, etc.). Implement a simple schema versioning system: for instance, store a version number in the database (maybe as an option like myplugin_db_version). On each plugin activation or load, check that against the current code’s expected version, and if it’s lower, perform upgrade steps then update the stored version. For example, if version 1.0 had an option myplugin_options and in 1.1 you split it into two options, your upgrade routine could detect old version “1.0”, then read the old option, migrate its values to new options, delete the old one, and set version to “1.1”. For custom tables, use the WordPress function dbDelta() which is specifically made to compare and alter table schemas safely. You would define your table SQL (with the new columns or indexes) and call dbDelta($sql) on plugin upgrade – it will create or modify tables to match the schema. Always wrap such operations in proper checks (and maybe backups or warnings if it’s destructive). Keep performance in mind: don’t run heavy migrations on every page load – only do it on admin or activation (some plugins trigger upgrades on an admin visit to avoid race conditions). Document your upgrade routine in changelogs so users know if an update will do a one-time DB operation. Also, test the upgrade path from an old version to new thoroughly, possibly with large data, to ensure it completes without timeouts. By engineering migrations, you allow your plugin to evolve without breaking existing installations. Many successful plugins use an internal version and an upgrade hook to manage this smoothly.
• /wp-dev:migration-plan [schema]
Key Points: Outline a migration plan for your plugin’s data [schema]. This includes specifying a current schema version (e.g., an integer or semantic version for your data model) and what changes are introduced at each version. For instance: “Schema v1: initial release, one table wp_myplugin with columns A, B. Schema v2: added column C to wp_myplugin. Schema v3: split settings option into two separate options.” Then describe how your code will detect and apply these changes. Example plan: “On plugin init, check option myplugin_db_version. If < 2, run function upgrade_to_v2() which uses dbDelta to add the new column; if < 3, run upgrade_to_v3() to migrate settings.” Note any special considerations, like copying data from an old structure to a new one, or populating new fields with default values. Also consider rollbacks: if a user downgraded the plugin, would your migration have irreversibly altered data? Ideally, design migrations that add non-breaking changes (like new columns that older versions would ignore) or at least warn users that once upgraded, going back is unsupported. A migration plan also involves testing on staging sites or backup sets of data. You might incorporate logging during migration (maybe using error_log or admin notices) to record that an upgrade happened – useful for support if something goes wrong. In summary, a migration plan ensures your plugin can handle upgrades gracefully, keeping user data intact and eliminating manual database fixes. It’s a mark of a mature plugin that doesn’t just assume fresh install every time.
• Scheduled tasks (Cron) — use WP-Cron events with safe duplication guards
If your plugin needs to perform periodic tasks (cleanup, external sync, email reports, etc.), use WP-Cron rather than system cron so it works across all hosting environments. Register a scheduled event with wp_schedule_event() on plugin activation (or on an admin action) specifying a recurrence and a custom hook name. Always check if the event is not already scheduled with wp_next_scheduled() before adding, to avoid duplicate scheduling. For example:
if (!wp_next_scheduled('myplugin_cron_hook')) {
wp_schedule_event(time(), 'hourly', 'myplugin_cron_hook');
}
Then hook a function to myplugin_cron_hook to do the work. Within that function, you might still implement a duplication guard (especially if there’s any chance the task could overlap or if multiple events could fire due to race conditions). Sometimes a transient “lock” is used: set a transient when task starts and skip if one is found already (to prevent re-entry). Ensure to unschedule your event on plugin deactivation or uninstall using wp_unschedule_event() or clear_scheduled_hook('myplugin_cron_hook'), so it doesn’t orphan. Also, use appropriate recurrence intervals – WP has built-in schedules like ‘hourly’, ‘twicedaily’, ‘daily’, or you can add custom intervals. Don’t schedule tasks more frequently than needed (and document if your plugin might need DISABLE_WP_CRON off or any special setup). WP-Cron runs when someone visits the site (or via CLI cron), so consider that reliability (on low-traffic sites, cron jobs may run late). If precise timing is critical, advise the admin to set up a real cron hit to wp-cron.php. In any case, by using WP-Cron, your plugin plays by WP rules and will run in a wide range of hosting setups. The key is to avoid duplicate executions and to clean up after itself, which you handle with the checks and unscheduling mentioned.
• /wp-dev:cron-plan [job]
Key Points: For each cron [job] your plugin needs, specify the schedule and safeguards. Define: When (e.g., “hourly at approximately :00 minutes”), What (the task description), and How (the hook name and function). For example: “Job: expire outdated entries – scheduled daily via WP-Cron on hook myplugin_expire_hook, runs myplugin_expire_function() which deletes entries older than 30 days.” Note the recurrence (daily) and any conditions inside the function (like it processes max X items per run to avoid timeouts). Include a plan for registration: “The event is scheduled on plugin activation if not already present.” And for removal: “It’s cleared on plugin deactivation/uninstall”. Also mention duplication guard if applicable: e.g., “Function uses a transient lock so if a previous run is still in progress, it will not start a new one.” If you have multiple jobs, list each separately. If your plugin allows user-configurable scheduling (say user can choose to run something hourly or daily), note that and how you’ll update the schedule (like unschedule old and schedule new on option change). By writing out this cron plan, you ensure no cron task is forgotten or behaves unpredictably. It also helps during debugging – knowing “myplugin_expire_hook” fires daily means if something related isn’t happening, you can check if the cron is registered. In sum, the cron plan makes background tasks predictable and well-managed in the WordPress Cron system.
• Logging & diagnostics — provide a status page and privacy-safe logs
Building in some diagnostics can greatly help in supporting your plugin. Consider adding a debug or status page in the admin (for admin eyes only, capability check manage_options). This page could show things like current plugin settings, environment info (WP version, PHP version, active theme), and whether required background processes (like cron events or external API connections) have run successfully. Providing an exportable log of key events or errors is valuable. You might maintain a simple log (in an option or custom table, or even a debug file) of recent plugin actions – e.g., “Mail sent to X at Y time” or “API sync failed with error Z at 12:00”. If implementing logging, ensure it’s privacy-aware: do not log personal data (or if you do for debugging, provide a way to erase it and mention it in the privacy policy). WordPress offers built-in tools for personal data export/erasure; if your logs contain personal info, integrate with those (using the hooks in the Privacy API   to erase such info on request). Also, wrap any debug logging so it only operates when WP_DEBUG or a specific constant is true, to avoid performance hit or exposing info in production. You can utilize error_log() for server logs (when WP_DEBUG_LOG is on) or store events in a custom post type or option for retrieval. Provide a button on the status page like “Download Debug Log” which compiles recent entries into a text file, so users can share it with you for support. Make sure to sanitize any output here (so, if showing an error message, escape it to prevent any rogue HTML from breaking the admin). Having a diagnostics page and logs can turn a 5-cycle support thread into a quick resolution, as you (and the user) can quickly see what’s going wrong (e.g., an API key is invalid as per log entry). Emphasize that these logs are for admin use, and do not publicly expose them (capability check and maybe nonces to download). This commitment to transparency and troubleshooting sets your plugin apart by making it easier to diagnose issues without digging into code.
• /wp-dev:diagnostics-screen [signals]
Key Points: Design a diagnostics or status screen that presents important [signals] about plugin health. Determine which signals matter: for instance, “Is the plugin connected to API?” (yes/no), “Number of items processed in last cron run”, “Last error message (if any)”, “Current version of plugin and DB schema”, etc. Each signal should be derived from data your plugin can gather. Plan how you’ll fetch and display these: you might use admin_notices for urgent warnings (e.g. “API key missing!”), but a dedicated page can have a section “System Status” with green/red indicators. For example: Email Send Status: OK (last sent 2026-02-02 14:00) or ERROR (could not connect to SMTP). Also list configuration info: e.g., “WP Memory Limit: 40M”, which can hint if resources are an issue. If your plugin has background tasks, show when they last ran or if one is queued. Ensure sensitive info (like API keys or user data) is either masked or omitted on this screen to keep it privacy-safe. Provide guidance on this page as well – e.g., “If you encounter issues, click ‘Export Debug Info’ to download technical details to share.” That export could include the signals plus sanitized config (like which add-ons enabled, etc.). Since this is a lot of info, structure it with headings and maybe make it collapsible for sections. By implementing [signals] monitoring, you effectively create a “health check” for your plugin. This not only helps in support scenarios but also builds trust with savvy users who appreciate transparency. Where possible, integrate with WordPress Site Health (you can add your plugin’s info to the Site Health info tab via filters) – for example, adding a section “MyPlugin” with key info. Summing up, a diagnostics screen turns implicit plugin state into explicit signals that can be acted on, greatly easing troubleshooting and maintenance.
⸻
WP Admin UX (WP-Admin patterns)
• Build the admin UI — menus, pages, forms, notices, using WP patterns
Aim to make your plugin’s admin interface feel like a natural extension of WordPress. Add menu items appropriately: if it’s a major feature, a new top-level menu might be fine; otherwise, tuck it under an existing section like Settings or Tools for less clutter. Use the WordPress administration menus API (add_menu_page, add_submenu_page) with proper capability checks (usually manage_options for settings) so only authorized users see them. When designing admin pages, follow core UI patterns: use the Settings API for forms (it will generate fields with proper markup), use WP styles (the default admin CSS) for any custom HTML so it looks consistent (e.g., use classes like regular-text on text inputs for consistent sizing). If showing lists of data, consider using List Tables (WP_List_Table class) to get the standard table styles with sorting, pagination, etc. Ensure to include the screen icon and page title in
as core does. Provide helpful admin notices for user actions: for example, after saving settings, add an admin notice “Settings updated” using the admin_notices hook and CSS classes like notice notice-success for success or notice notice-error for errors. Make notices dismissible if they are persistent. Also adhere to accessibility: every form field needs a (use