# Performance Program — PCDesigner

A standing program for keeping the plugin fast on 3G mobile and on shared hosting.

## 1. Targets

| Surface | Metric | Target | Today (estimate) |
|---|---|---|---|
| Storefront product page | LCP (3G mobile) | ≤ 3.5 s | red |
| Studio editor | TTI | ≤ 5 s | red |
| Save design AJAX | p95 | ≤ 1.5 s | amber |
| Sync PDF download (≤ A4) | p95 | ≤ 4 s | amber |
| Async PDF batch | throughput | ≥ 30 jobs/min on 2 vCPU | unknown |
| Admin order detail | TTFB | ≤ 800 ms | amber |
| Plugin JS on storefront product page (gzip) | size | ≤ 250 KB | red |

Measure baselines in production with WebPageTest (Moto G4, 3G Fast, Singapore + Sao Paulo + Frankfurt).

## 2. Front-end weight breakdown

Approximate min sizes of `assets/libs/`:

```
fabric.2.6.0.min.js          ~290 KB
angular-1.6.9.min.js         ~166 KB
snap.svg.js                  ~246 KB
opentype.min.js              ~163 KB
html2canvas.min.js           ~161 KB
firebase.min.js              ~140 KB
lodash.js                    ~225 KB
jquery-ui.min.js             ~224 KB
Chart.min.js                 ~156 KB
moment.min.js                ~51 KB
perfect-scrollbar.min.js     ~25 KB
```

Plus custom: `bundle-modern.min.js`, `printcart-jq.js` (95 KB), `printcart-ext.js` (83 KB), `designer*.min.js`.

## 3. Cuts (priority order)

### Cut 1 — Don't ship Angular on storefront
Angular 1.6 is admin-only. Verify in `class-pc-script-hook.php` that storefront enqueue does not include Angular bundles. Estimated saving: 166 KB gzip on every product page.

### Cut 2 — Lazy-load Fabric
Load Fabric only when the user clicks "Customize" or scrolls past the product hero. Use `import()` dynamic load behind a button. Saving: ~80 KB gzip on initial product paint.

### Cut 3 — Lazy-load Three.js + GLTF
3D preview rarely opens. Load `assets/js/3d/*` on first 3D button click. Saving: ~150 KB gzip.

### Cut 4 — Lazy-load Firebase
Live chat library should bind only when chat icon is clicked. Saving: ~140 KB gzip.

### Cut 5 — Lodash → modular imports
Replace `lodash.js` with `lodash-es` per-function imports. Saving: ~150 KB gzip.

### Cut 6 — html2canvas + opentype on demand
Both rarely needed at first paint.

### Cut 7 — Remove jQuery UI from storefront
Use vanilla equivalents. Saving: ~70 KB gzip.

Cumulative target: drop 600 KB+ gzip from storefront product page.

## 4. Bundle plan

Move from gulp 3 + webpack 4 (`assets/package.json`) to:

- esbuild or vite for new entrypoints.
- Two output bundles: `editor.<hash>.js` (Fabric + tools), `storefront.<hash>.js` (cart hooks only).
- Code splitting for niche modules.
- Long-term cache headers via filename hashing.

Migration steps:

1. Add `assets-v2/` next to current `assets/`.
2. Build a single new entrypoint (storefront cart integration).
3. Verify no regression on storefront.
4. Migrate editor next.
5. Switch `class-pc-script-hook.php` to enqueue v2 paths behind a feature flag.
6. Remove old assets after a soak period.

## 5. PHP / DB hot spots

Files to profile first:

- `includes/class.nbdesigner.php` — cart hooks run on every cart pageview.
- `includes/class-printcart-api.php::check_tier` — must cache.
- `includes/class-api.php` — gallery generator.
- `includes/class.product-builder.php` — admin saves are heavy.
- `includes/options/admin-options.php` — autoload bloat.

Wins:

- **Transients around any HTTP call**, TTL 5 min default.
- **Move daily license cron to once-a-day with backoff**.
- **Index `_nbdesigner_enable` lookups** — denormalise into a small table updated on `save_post`.
- **Audit autoloaded options**: anything > 20 KB → `autoload=no`.
- **Avoid reading order meta via `get_post_meta`** under HPOS — uses `posts`/`postmeta` fallback that defeats the point.

Profiling commands:

```bash
# Slow query log via Query Monitor
# In wp-admin go to Query Monitor → Queries by Caller → filter by 'web-to-print-online-designer'

# Autoload audit
wp option list --autoload=on --fields=option_name,size --format=csv \
  | sort -t, -k2 -n -r | head -50

# Find slow callers
wp eval-file tools/profile-callers.php   # add this script per release
```

## 6. Output pipeline

Order of operations:

1. Log timestamps in `class-output.php` for: input parse, font load, image embed, PDF render, file write.
2. Confirm Imagick is used when available (3× faster than GD).
3. Resize source images **before** TCPDF embeds.
4. Strip EXIF on non-print exports.
5. Cache rendered preview thumbnails keyed by design hash.
6. Move heavy work to `WP_Background_Process`; verify worker concurrency in `background-processes/export-pdf.php`.

## 7. Caching layer

- **Object cache**: support Redis / Memcached. Wrap any `get_option`-heavy access through a thin helper that prefers wp_cache_get.
- **Page cache**: ensure plugin endpoints are excluded from full-page caches (cart, AJAX). Document in support docs.
- **CDN**: serve `wp-content/uploads/nbdesigner/` static assets through CDN; gallery thumbs especially.

## 8. Async / queue

- Preview generation in marketplace already runs through `class.generate.preview.process.php`.
- PDF export already uses `WP_Background_Process`.
- New heavy work (AI, 3D bake, analytics aggregation) must follow the same pattern — never run synchronously in admin.

## 9. Editor (Fabric)

- Cap canvas raster resolution at 4× display.
- `objectCaching = true` on idle shapes.
- Throttle `mouse:move` to `requestAnimationFrame`.
- Debounce curve text recompute by 150 ms.
- Avoid re-rendering whole canvas on color change — patch only the affected object.

## 10. Network discipline

- **No third-party CDN** unless we control a sub-resource integrity hash.
- **Preconnect** to the 3 endpoints we always hit: `api.printcart.com`, `fonts.googleapis.com`, `firebaseio.com`.
- **Defer + async** non-critical scripts.
- **Compress** WebP for previews; keep PNG/TIFF only for print masters.
- **HTTP cache headers**: hash filenames, 1y immutable cache; bust by hash.

## 11. Monitoring

Add basic real-user monitoring (RUM) once telemetry MVP ships:

- LCP, FID, CLS, TTFB per surface.
- Editor canvas-ready event.
- AJAX latency p50 / p95.
- PHP error rate.
- PDF render queue depth.
- Outbound HTTP failure rate per service (Printcart / Firebase / S3).

Alert on:

- p95 editor TTI > 7 s for 30 min.
- Background queue depth > 1000 for 30 min.
- Outbound failure rate > 5% for any service for 15 min.

## 12. Cost discipline

Performance and cost are entangled.

- Cache aggressively to avoid Cloud round-trips per pageview.
- Compress before storage; rehydrate on demand.
- Tier rate-limits on AI / cloud render.
- Periodic prune of `wp-content/uploads/nbdesigner/temp/` (cron daily).
- Move cold designs (unused 12+ months) to Glacier-like tier.

## 13. Reporting cadence

- Daily: Slack summary of Sentry errors + queue depth.
- Weekly: LCP / TTI dashboard reviewed by lead.
- Monthly: bundle-size diff per release; fail CI when > 5% growth without justification.
- Quarterly: re-baseline targets.

## 14. CI gates

Add to release pipeline:

```bash
# JS bundle size
node tools/bundle-size.js --max-storefront=250kb --max-editor=600kb

# PHP lint
find includes -name '*.php' -print0 | xargs -0 -n1 php -l

# WPCS
phpcs --standard=WordPress --severity=5 includes/

# Smoke E2E (Cypress)
npx cypress run --record
```

## 15. Anti-patterns

- Adding a new big lib without a budget review.
- Running `wp_remote_get` inside an admin pageview without a transient.
- Putting heavy work into `init` or `wp_loaded` hooks.
- Sending 8000×8000 source images to TCPDF.
- Writing intermediate artefacts to `nbdesigner/uploads/` and never cleaning up.
- Polling AJAX from the editor every second to "save".
