1. Register hooks with intent
I only attach to init, admin_init, rest_api_init, or front-specific hooks when the work truly belongs there. Heavy setup waits until the request needs it—admin-only code behind is_admin() checks, REST controllers registered once, no option reads in tight loops.
2. Queries: batch, cache, measure
I design list screens and sync jobs to avoid N+1 meta or term queries. When data is read-heavy and rarely changes, I use transients with explicit invalidation or wrap expensive reads with the object cache when available. I still assume no persistent cache on cheap hosting—the plugin must not fall over if Redis disappears.
3. Assets load only where they are used
Scripts and styles enqueue on known admin screens or when a shortcode/block is present—not globally on wp_enqueue_scripts for every visitor. That single habit fixes more Lighthouse regressions than micro-optimizing PHP syntax.
4. Structure for deletion and upgrades
I ship uninstall routines that match the data policy: remove custom tables or anonymize logs if the contract says so. Migrations are versioned (option or db schema version constant) so activation does not re-run destructive steps. Namespaces and PSR-4 autoloading keep classes small enough to reason about when a feature is removed.
5. Tests protect both performance refactors and features
Pure services get unit tests; WordPress boundaries get integration tests where the project budget allows. The point is not 100% coverage—it is confidence that changing a query or cache key will not silently break checkout or permissions.
6. Observability without log spam
I log actionable events (sync failure, API 429) behind a debug flag or structured logger, not every successful request. Ops teams thank you when error channels stay quiet enough to notice real fires.
Performance and maintainability in WordPress plugins are the same discipline: do less by default, make side effects obvious, and leave a clean exit when the plugin is turned off. Everything else is commentary.