WordPress: Website development, e-commerce, and custom solutions
A client arrives with a ready-made WordPress site — and the first thing I see in DevTools: 47 active plugins, the page weighs 6.8MB, Time to First Byte 2.4s, and the console shows five conflicting jQuery versions. This is not rare. This is the standard of a "finished" site that grew from a template into something alive but unmanageable.
WordPress holds 43% of the CMS market — not because it's ideal, but because it's predictable, extensively documented, and has a plugin ecosystem for any task. The engineer's job is to use this ecosystem carefully, without turning the site into a garbage heap of dependencies.
Where WordPress breaks down most often in production
Three scenarios I encounter most frequently:
Rendering blocked by plugins. Plugin A loads jQuery 3.6, Plugin B — jQuery 1.12, the theme — its own jQuery Migrate. As a result, wp_enqueue_scripts delivers three different versions of the library, rendering is blocked for 800ms before the main content is parsed. Fixed through wp_dequeue_script, centralized dependency control, and moving non-critical scripts to defer/async.
N+1 in custom queries. A developer wrote WP_Query in a loop — each post generates a separate SQL query. On a page with 20 posts, that's 21+ database queries. MySQL starts slowing down, the server heats up. Fixed through post__in with prefetch or by switching to wpdb->get_results() with JOIN. Query Monitor is the first tool for diagnostics.
WooCommerce under load. A store with 15,000 SKU, without object caching, without Redis — at 200 concurrent users, wc_get_product() kills the database. WordPress transient cache doesn't help: it writes to the database, increasing load. Real solution — Redis through wp-redis or Memcached, plus wp_cache_set()/wp_cache_get() in custom code.
Stack and approaches in our work
Theme development. We don't use page builders like Elementor for product sites — they generate bloated HTML and lock the client to the visual editor forever. Instead: custom theme based on _s (underscores) or a block theme for Full Site Editing, Tailwind CSS via Vite, TypeScript for any complex JS.
Gutenberg and block development. Starting with WordPress 5.0, Gutenberg is not an editor, it's a platform. We develop custom blocks through @wordpress/scripts, register through register_block_type() with block.json. Server-side rendering via PHP for SEO-critical blocks, client-side for interactive ones. Inner Blocks for composite components.
REST API and headless. WordPress as a headless CMS through WP REST API v2 or WPGraphQL. Typical scheme: WordPress on a subdomain cms.example.com, Next.js frontend on the main domain. ISR (Incremental Static Regeneration) for blog pages — the page regenerates in the background when accessed after revalidate expires, without blocking the user. For authenticated requests — JWT through jwt-authentication-for-wp-rest-api or Application Passwords (built-in since WP 5.6).
WooCommerce. Extended through hooks and filters — never edit core files directly. Custom product types through WC_Product extension. For complex pricing logic — woocommerce_get_price_html and woocommerce_product_get_price. Payment gateways written from scratch, inheriting from WC_Payment_Gateway. Integration with 1C — through CommerceML or custom REST endpoint.
Performance. Mandatory stack: Redis Object Cache + Full Page Cache (LiteSpeed Cache or WP Rocket) + CDN for static content + WebP through add_image_size() with conversion. Native lazy load (loading="lazy") plus custom for critical images above the fold — preload through <link rel="preload">.
Case study: WooCommerce store, LCP 9s → 1.8s
Electronics store, 40,000 SKU, WooCommerce + custom theme. PageSpeed Insights: LCP 9.2s, CLS 0.41, INP 680ms.
Diagnosis:
- Hero image 3.8MB JPEG, unoptimized, no
srcset - 23 plugins loaded JS/CSS on every page, including product pages
-
wc_get_product()called 60 times on category page without caching - Fonts loaded through Google Fonts (additional DNS lookup)
What we did:
- Hero — WebP 180KB,
<img fetchpriority="high" decoding="async">,srcsetfor 3 breakpoints - Conditional plugin loading through
is_product(),is_cart(),is_checkout()— removed 80% of unnecessary JS - Redis Object Cache,
WC_Productprefetch throughwc_get_products()withinclude - Fonts — self-hosted via
@font-face,font-display: swap - CLS fixed through
aspect-ratioon all product card images
Result: LCP 1.8s, CLS 0.04, INP 140ms. Core Web Vitals — green.
Process
Audit and analytics. For a new project — analysis of existing codebase (if any), competitors, technical requirements. For a new site — analytics on semantics, UX prototyping.
Architecture. We decide: monolithic WordPress or headless. We define data types: Custom Post Types, Custom Fields (ACF or native register_meta()), taxonomies.
Development. Local by Flywheel or Docker (nginx + php-fpm + MariaDB) for local environment. Git with pre-commit hooks for PHP CS Fixer and ESLint. Deploy through WP-CLI + SSH or via Buddy.works CI/CD.
Testing. PHP Unit for custom plugins. Playwright for E2E of critical scenarios (add to cart → checkout → confirmation). Lighthouse CI in pipeline — fail if Performance Score < 85.
Deployment and support. Staging via WP Stagecoach or manual clone. Monitoring — UptimeRobot + Sentry for PHP errors. Plugin updates — through WP-CLI in test environment first.
Timeline guidelines
| Project type | Timeline |
|---|---|
| Landing page on custom theme | 2–3 weeks |
| Corporate site (10–30 pages) | 4–8 weeks |
| WooCommerce store (basic) | 6–10 weeks |
| WooCommerce + custom logic + integrations | 3–6 months |
| Headless WordPress + Next.js | 8–16 weeks |
Cost calculated individually after audit of requirements.
Common WordPress development mistakes
Direct theme file editing. When the theme updates, all changes are lost. Always — child theme or fully custom.
update_post_meta() in a loop. Each call — separate UPDATE. For bulk operations — $wpdb->update() or update_metadata_by_mid().
Disabled WP_DEBUG in development. Hidden PHP Notices clutter the error log and often point to real logic problems.
Storing media in Git. wp-content/uploads — in .gitignore, synchronization via WP-CLI media import or rsync.
No limit on WP_Query. posts_per_page => -1 on a page with thousands of records — guaranteed timeout.







