# Kutby Performance Standard

**Status:** Binding engineering contract  
**Applies to:** Every feature, module, component, template, admin page, and asset  
**Theme:** Kutby  
**Audience:** Contributors, reviewers, and future maintainers  

This document is not optional guidance. It is the performance law of the Kutby project.

Kutby must **feel extremely fast** — on the public frontend **and** inside WordPress Admin.

Performance is a **core architecture requirement**, not a later optimization phase.

---

## 1. Performance Philosophy

Kutby is built for Arabic educational platforms. Users open many pages, teachers live in Admin, and students expect instant navigation. Speed is part of product quality.

### Core beliefs

1. **Performance before convenience.** A convenient pattern that burns queries, bytes, or main-thread time is rejected.
2. **No feature is accepted if it noticeably hurts performance.** “It works” is not enough.
3. **Every request matters.** Prefer zero network work over “maybe later.”
4. **Every query matters.** Prefer zero SQL over “it’s only one more.”
5. **Every kilobyte matters.** Prefer deletion over compression theater.
6. **Every repaint matters.** Prefer calm layouts over decorative motion that costs frames.
7. **Every dependency matters.** Prefer native APIs and existing Kutby modules over new libraries.

### Default stance

When two solutions both solve the product need, **choose the faster one**. When measurement is missing, **assume cost until proven cheap**.

---

## 2. Frontend Performance Rules

### Load only what is needed

- Enqueue assets for the **current template / component context**, not the whole site.
- Do not ship homepage CSS/JS to single posts, archives, search, or 404 unless shared by design.
- Prefer **component-based asset loading**: a component that is not rendered must not load its exclusive CSS/JS.

### JavaScript

- **No global JS** unless it is required on essentially every page (e.g. minimal navigation/accessibility shell).
- Prefer **native browser APIs** (`fetch`, `IntersectionObserver`, `matchMedia`, `AbortController`) over libraries.
- **Defer** theme scripts by default.
- Use **async** only when order independence is proven safe.
- Prefer **CSS** for animation. Use JS animation only when CSS cannot express the interaction.
- Prefer `transform` / `opacity` over animating `top`, `left`, `width`, or `height`.
- Avoid layout thrashing: do not interleave forced reads and writes of layout properties in loops.

### CSS

- **No global CSS for page-specific components.** Shared tokens/base/layout only; page and feature styles stay scoped.
- Prefer design tokens / CSS variables from the Kutby token system — no ad-hoc hardcoded design values in new UI.
- Avoid unused selectors, duplicate rules, and copy-pasted component skins.

### Media & layout stability

- **Lazy-load** images below the fold (`loading="lazy"` where appropriate).
- **Lazy-load** iframes; never auto-load heavy embeds above the fold without product justification.
- Always provide **width and height** (or equivalent aspect-ratio CSS) for media to **prevent CLS**.
- Reserve space for async UI (skeletons only when needed; prefer stable layout).

### Icons & fonts

- **SVG** (inline or local sprite/files) over icon fonts.
- **Local assets only.** No CDNs for runtime CSS/JS/fonts/icons.
- **No external fonts.** Tajawal (or approved local theme fonts) only, with `font-display: swap`.

### Dependencies

- No frontend jQuery.
- No page builders, Bootstrap, Tailwind runtime, Font Awesome, or Google Fonts CDN.
- Do not add a library for a task solvable in < ~30 lines of clear theme code.

---

## 3. Admin Performance Rules

Admin speed is a first-class product requirement. Teachers and operators spend hours in **كتبي / Control Center**. Opening admin pages must **always feel instant**.

### Page load

- **No expensive queries during admin page load** for decorative or secondary widgets.
- **No `COUNT(*)` on large tables** without a cache layer and a clear invalidation story.
- Dashboard / overview widgets **must use cache** (transient or object cache) with a defined TTL.
- Prefer reading **already-loaded Theme Options** (Manager cache) over re-querying storage.

### Data & interaction

- Large datasets **must paginate** (or virtualize later if justified). Never dump unbounded lists into the DOM.
- Search inputs **must debounce** before firing AJAX or filtering large collections.
- AJAX responses must stay **lightweight** (JSON payloads only; no HTML dumps of entire panels unless unavoidable).
- **No synchronous heavy processing** on request threads (import parsing, bulk regenerate, remote sync).
- **Import/export must never block the UI.** Use staged requests, background jobs, or chunked processing.
- Prefer **background / deferred processing** whenever work exceeds a few hundred milliseconds.

### Admin assets

- Load color picker, media modal, and other WP utilities **only on screens that need them**.
- Admin Design System CSS/JS must remain lean; Showcase and developer tools must not ship to production users when disabled.

### Progressive Admin rendering

- Admin pages should **render immediately**. First paint must not wait on secondary widgets or optional aggregates.
- Heavy widgets must **not delay first paint**. Prefetch or AJAX-load expensive panels after the shell is visible when the data is secondary.
- Secondary information may load **asynchronously** when appropriate (cached stats, system probes, non-critical tips).
- **Perceived performance** is as important as measured performance. Instant chrome with deferred enrichment beats a blank wait for a perfect payload.

---

## 4. Database Rules

### Query discipline

- **No queries inside loops.** Collect IDs, then batch (`WP_Query` with `post__in`, `get_posts`, term meta APIs, custom SQL with `IN (...)`, etc.).
- Prefer **one well-shaped query** over many opportunistic ones.
- **Avoid duplicate queries** in the same request (memoize in request statics / Manager / dedicated helpers).
- **Cache repeated lookups** within the request; use persistent cache when data is shared across requests.

### Options & autoload

- Theme Options must use a **single option load** path (`Manager` / Field Registry), not repeated `get_option()` calls for the same key.
- Do **not** autoload large or infrequently used option blobs. Autoload is a privilege, not a default.
- Avoid storing derived HTML in options; store data and render when needed (or cache the derived output separately with TTL).

### Theme Options access rules

- **Load once. Read many. Write once.** Resolve options at the start of the request path that needs them; do not re-resolve per field, per component, or per template partial.
- Avoid repeated option resolution helpers that hide additional `get_option()` or full-array rebuilds.
- `Manager` cache is the **single source of truth** for in-request Theme Options reads. Field Registry / Schema define structure; they must not bypass Manager for storage reads.
- Writes go through the established save pipeline (validate → sanitize → save → invalidate). Do not scatter `update_option()` calls that skip cache flush and events.

### Schema & health

- Add **indexes** where custom tables or meta queries are introduced and justified by measurement.
- Profile slow queries (Query Monitor, `SAVEQUERIES` in local, host slow-query logs).
- Batch writes (imports, migrations, regenerations) with chunking and progress, never unbounded loops.

---

## 5. Rendering Rules

### Markup

- Prefer a **minimal DOM**.
- Avoid deeply nested wrappers “for styling convenience.”
- **Reuse Admin/Frontend Components** — do not duplicate HTML structures.
- No unnecessary wrappers around Components that already provide structure.
- Semantic HTML first; decorative nodes only when required for accessibility or layout.

### Paint & interaction

- Minimize repaint/reflow: batch class changes; avoid reading layout inside event storms.
- Prefer CSS containment / simple stacking contexts for isolated interactive regions when beneficial.
- Do not animate large document trees.

---

## 6. Assets

### Build pipeline

- **Vite only** for theme production bundles.
- Prefer **hashed** filenames via the Vite manifest for cache busting.
- Support **logical split**: frontend entry vs admin entry (and future feature entries only when justified).
- **Tree-shake** and remove dead code in source modules.

### Runtime hygiene

- **No duplicate CSS** for the same component across entries.
- **No duplicate JS** modules registered twice under different handles.
- Remove unused source files from the build graph; do not leave orphaned legacy assets as defaults when dist exists.
- Fallback assets (`assets/css`, `assets/js`) exist for resilience — production releases must ship a clean `assets/dist` build.

### Local-only policy

- Theme runtime assets live under the theme. No remote CSS/JS/font URLs in production templates or enqueue calls.

### Production build expectations

Production builds (`npm run build` → `assets/dist` + manifest) must contain:

- **No debug helpers** and no development-only branches left enabled by default
- **No development code** paths required for HMR / Vite client in production
- **No console logging** in shipped JS
- **No dead code** that the bundler can eliminate and that source still needlessly retains as live exports
- **No unused assets** enrolled as default enqueues
- **Only optimized bundles** — hashed, minified where configured, tree-shaken

`KUTBY_DEV` / Vite HMR are local development tools. They are not production behavior.

---

## 7. Caching

### Strategy overview

| Layer | Use for | Notes |
|-------|---------|--------|
| Request memoization | Theme Options, registries, manifests | Static/in-memory within one request |
| Transients | Dashboard stats, expensive admin aggregates | Always define TTL + invalidation |
| Object Cache | Shared lookups when available | Theme must remain correct without Redis/Memcached |
| HTML/page cache (host/plugin) | Public pages | Theme must not break when full-page cache is on |

### What should be cached

- **Theme Options** — single load via Manager; flush on save (`kutby_options_saved` / internal events). Load once, read many, write once; Manager cache remains authoritative for the request.
- **Menus** — rely on WP core menu caching; avoid rebuilding menu trees repeatedly per request.
- **Breadcrumb** — cache derived trails when generation is non-trivial; invalidate on content/term changes as needed.
- **Statistics / dashboard metrics** — transient-backed; never live `COUNT(*)` on every Admin load.
- **Navigation / sidebar expensive fragments** — cache when queries are heavy; key by context (user role, page type) carefully.
- **Asset manifest** — load once per request.

### Invalidation

- Every cache **must** document:
  - cache key shape
  - TTL
  - invalidation triggers (option save, post update, term change, menu update, import complete)
- Prefer **event-driven invalidation** (Theme events / WP hooks) over hoping TTL alone is enough for correctness-critical data.
- Never cache personalized Admin notices or nonce-bearing markup in shared caches.

### Compatibility

- Code must work with **no persistent object cache**.
- When object cache is present, prefer it for hot shared reads without assuming it exists.

---

## 8. Developer Rules

### Pull Request performance questionnaire

Every PR that touches runtime behavior must answer:

1. **How many queries were added?** (typical Admin page + typical frontend page)
2. **How much JS was added?** (approx. KB gzipped, and which entry)
3. **How much CSS was added?** (approx. KB gzipped, and which entry)
4. **Any blocking rendering?** (sync scripts, font FOIT, large above-the-fold images without dimensions)
5. **Any unnecessary dependency?**
6. **Can this be cached?**
7. **Can this be lazy loaded?**

If answers are unknown, the PR is not ready for merge.

### Review stance

Reviewers may reject a PR solely for performance contract violations, even when the feature works.

---

## 9. Performance Budget

Budgets are **ambitious but realistic** for a modular educational theme on shared hosting / XAMPP-class local and typical production PHP/MySQL.

Targets are for **production builds** (`assets/dist`), cold-ish cache, typical content, no page builder, no third-party bloat.

### Frontend budgets

| Metric | Target | Hard ceiling |
|--------|--------|--------------|
| Homepage HTML document size (uncompressed) | ≤ 40 KB | 70 KB |
| Critical above-the-fold CSS (effective) | ≤ 25 KB gzipped | 40 KB gzipped |
| Total theme CSS (gzipped, homepage) | ≤ 35 KB | 55 KB |
| Total theme JS (gzipped, homepage) | ≤ 15 KB | 30 KB |
| Local webfonts transferred (all weights used on page) | ≤ 60 KB | 90 KB |
| Theme-initiated network requests (HTML + CSS + JS + fonts; excl. content media) | ≤ 12 | 18 |
| SQL queries — homepage (theme-related, typical) | ≤ 20 | 35 |
| SQL queries — single post (theme-related, typical) | ≤ 25 | 40 |
| LCP (lab, mid-tier mobile, local assets) | ≤ 2.5 s | 4.0 s |
| CLS | ≤ 0.05 | 0.10 |
| INP / interaction delay (lab) | ≤ 200 ms | 400 ms |

### Admin budgets

| Metric | Target | Hard ceiling |
|--------|--------|--------------|
| Kutby Control Center first paint feel | Instant (< 300 ms perceived) | Soft fail if consistently sluggish |
| Theme Options panel document + assets ready | ≤ 1.5 s local | 3.0 s |
| SQL queries — typical Kutby options panel | ≤ 30 | 50 |
| AJAX save / regenerate CSS JSON payload | ≤ 50 KB | 150 KB |
| Dashboard widget data freshness via cache | TTL 5–60 min (feature-defined) | No uncached heavy aggregate on every load |

### Runtime performance budget (PHP theme overhead)

These numbers measure **Kutby theme overhead only** — Module Manager boot, theme modules, Theme Options reads, Asset Manifest Manager, Tokens, template tags, and theme hooks. They **exclude** WordPress Core bootstrap, third-party plugins, and host latency.

| Context | Target | Hard limit |
|---------|--------|------------|
| Frontend theme runtime | < 30 ms | 60 ms |
| Admin theme runtime | < 80 ms | 150 ms |

Profile with tools that can attribute time to theme callbacks (e.g. Query Monitor / custom timers around theme boot). If Core or plugins are slow, fix or isolate them — do not “spend” the theme budget on non-theme work, and do not hide theme cost inside Core blame.

### Memory budget

Memory efficiency is part of performance quality. Bloated arrays, repeated option copies, and unbounded HTML concatenation fail this contract even when the page “feels fine” on a developer machine.

| Context | Target | Hard limit |
|---------|--------|------------|
| Homepage (theme-attributable delta) | < 8 MB | 16 MB |
| Admin Kutby screens (theme-attributable delta) | < 16 MB | 32 MB |

Measure theme-attributable growth where possible (before/after theme boot or feature toggle). Absolute process RSS includes Core and plugins and is not a substitute for theme discipline.

### Notes on budgets

- Content images, video, and third-party plugins are **outside** theme budgets but must not be used as an excuse to ignore theme costs.
- If a feature needs to exceed a budget, it needs an explicit **budget exception** in the PR with measurement and a removal/mitigation plan.

---

## 10. Things Explicitly Forbidden

The following are **never acceptable** in Kutby without an approved, time-boxed exception:

1. **Query inside a loop** over posts/terms/users/rows.
2. **Blocking AJAX** that freezes Admin UI for multi-second work.
3. **Global assets for local features** (enqueue site-wide for a single template component).
4. **Large libraries for simple tasks** (date libs, icon frameworks, animation frameworks, UI kits).
5. **Repeated `get_option( KUTBY_OPTION_KEY )`** (or equivalent) instead of Manager / request cache.
6. **Repeated `wp_remote_get()` / remote calls** on page render without cache.
7. **Inline duplicated CSS** copy-pasted across templates.
8. **Inline duplicated JS** copy-pasted across templates.
9. **Loading unused libraries** “just in case.”
10. **External fonts, CDNs, or remote icon kits** in production theme paths.
11. **Frontend jQuery** or page-builder dependencies.
12. **Unbounded Admin lists** without pagination.
13. **Live `COUNT(*)` on large tables** on every Admin load.
14. **Autoloading huge option payloads** that are rarely needed.
15. **CLS-prone media** without dimensions / reserved space.
16. **Remote HTTP during normal rendering** (`wp_remote_get()`, `wp_remote_post()`, license pings, telemetry, analytics, version checks) — see Section 12.
17. **Directory scans / recursive filesystem search** during render — see Section 13.
18. **Debug dumps in production paths** (`dump()`, `dd()`, `var_dump()`, `print_r()`, `console.log()`) — see Section 19.

---

## 11. Future Performance Checklist

Before merging any feature, verify:

- [ ] **Queries reviewed** — count, N+1 risk, indexes, batching
- [ ] **Assets reviewed** — entry points, size, duplication, defer/async
- [ ] **Cache considered** — request memo, transient, invalidation hooks
- [ ] **Lazy loading considered** — images, iframes, non-critical JS
- [ ] **Accessibility preserved** — speed work must not break keyboard/screen-reader behavior
- [ ] **Memory impact acceptable** — no unbounded arrays or HTML string concatenation bombs
- [ ] **Admin performance unaffected** — Control Center still feels instant
- [ ] **Frontend performance unaffected** — budgets still held or exception documented
- [ ] **Dependencies justified** — native/API/existing module preferred
- [ ] **PR questionnaire answered** — Section 8 complete
- [ ] **PHP runtime / memory budgets considered** — Section 9
- [ ] **No runtime remote requests** on render paths — Section 12
- [ ] **No render-time filesystem scans** — Section 13
- [ ] **Hooks justified** — Section 17
- [ ] **Module work stays lazy / demand-driven** — Section 18
- [ ] **Regression review completed** when the change is significant — Section 20

---

## 12. Zero Runtime Remote Requests

The theme must **never** perform remote HTTP requests during **normal frontend rendering**.

The theme must **never** perform remote HTTP requests during **normal admin rendering**.

### Forbidden during rendering

- `wp_remote_get()`
- `wp_remote_post()`
- License validation against remote endpoints
- Telemetry
- Analytics beacons initiated by the theme
- Version / update checks against remote APIs

### Allowed remote communication

Remote communication is only allowed through:

- **Explicit user actions** (e.g. an admin clicks “Check connection” or imports from a URL they provided)
- **Background tasks**
- **Scheduled jobs** (`wp_cron` / Action Scheduler–style queues when introduced)

Never during page rendering. Never “just to be helpful” on `init`, `admin_init`, or template load.

---

## 13. Filesystem Rules

- **Never scan directories during runtime** rendering or enqueue.
- **Never recursively search files** while rendering templates or Admin panels.
- **Never build file indexes dynamically** on each request (icon trees, CSS inventories, template discovery walks).
- **Always use generated manifests** (Vite `manifest.json` via Asset Manifest Manager) and known paths.
- Filesystem discovery and inventory work belongs to **build time**, not render time.
- Reading a single known file (manifest, token CSS, one SVG by registry path) is acceptable; searching for “whatever exists” is not.

---

## 14. Object Lifecycle

- Avoid unnecessary object instantiation on hot paths.
- Do not create heavy services repeatedly per field, per widget, or per loop iteration.
- Prefer the existing **Theme singleton**, **Module Manager**, and module instances registered once per request.
- Avoid object creation **inside loops** when a reused helper/service suffices.
- Reuse immutable value-like data (token maps, schema definitions, manifest entries) via static/request caches rather than rebuilding graphs.
- Registration should stay cheap; expensive construction belongs behind demand-driven boot paths (see Section 18).

---

## 15. JSON Handling

- **Decode once** per request for a given payload (Vite manifest, stored JSON blobs, import payloads).
- **Reuse** the decoded structure through Asset Manifest Manager / request statics / Manager-level caches.
- **Cache parsed data** for the lifetime of the request (and persistently only when invalidation is defined).
- Avoid repeated `json_decode()` calls on the same source during the same request.

---

## 16. Regular Expressions

- Avoid expensive regex execution during rendering hot paths (template loops, menu walkers, content filters on every post).
- Prefer plain string operations (`str_contains`, `str_starts_with`, `explode`, prefix checks) when they are sufficient and clearer.
- Compile or define patterns **once** (class constants / statics) whenever reusable; do not rebuild large pattern sets per iteration.
- Content filters that must use regex need a cost justification and, when possible, early bailouts.

---

## 17. WordPress Hooks Budget

Expensive callbacks must **never** be attached unnecessarily. Hooks are not free; each listener runs in the critical path of many requests.

Pay special attention before attaching work to:

- `init`
- `wp`
- `template_redirect`
- `admin_init`
- `admin_menu`
- `current_screen`
- `save_post`
- `shutdown`

### Rules

- Every hook callback must **justify its runtime cost**.
- Prefer narrow hooks (`kutby/*` events, screen-specific Admin hooks, template-specific actions) over global early hooks.
- Do not attach heavy logic to `init` / `admin_init` “because it is convenient.”
- `shutdown` is for cleanup and deferred cheap work — not hidden business logic that should have been backgrounded.
- When a callback is only needed on Kutby Admin screens or a single template, gate it before doing real work (or attach it later on the correct hook).

---

## 18. Module Loading Rules

Kutby boots through **Module Manager**. That architecture must stay cheap.

- Modules should initialize **lazily** where work is not required for the current request type (frontend vs Admin vs AJAX).
- Do **not** initialize services that are not needed during the current request (e.g. Admin UI Components wiring on pure frontend requests beyond what register already gated).
- **Registration is cheap** — hook attachments and class construction without I/O.
- **Initialization / boot side effects should be demand-driven** — run expensive setup only when the request will use that capability.
- New modules must follow `register()` / `boot()` discipline already established; do not reintroduce centralized “load everything” constructors inside `Theme`.
- Feature modules remain the extension point; do not bypass Module Manager with ad-hoc bootstrap in `functions.php`.

---

## 19. Debug Code Policy

Explicitly forbidden in production theme paths and production builds:

- `dump()`
- `dd()`
- `var_dump()`
- `print_r()` used as debugging output
- `console.log()` / `console.debug()` / `console.warn()` left in shipped JS
- Any debug helper that executes unconditionally in production

Temporary debug is allowed only in local development and must be removed before merge. Debug gated behind explicit developer constants is acceptable only when it has **zero cost** when disabled (no expensive setup before the gate).

---

## 20. Performance Regression Policy

Every significant feature must be reviewed for regressions.

Review at least:

- PHP runtime (theme overhead)
- Memory
- Queries
- Assets (CSS/JS weight and request count)
- Admin responsiveness
- Frontend responsiveness

If any metric becomes worse, the Pull Request **must document**:

- **Why** the regression exists
- **Impact** (who feels it, how often, how much)
- **Mitigation** (cache, lazy load, split, remove, budget exception with sunset plan)

Silent regressions are contract violations. “We will optimize later” is not mitigation.

---

## Enforcement

- This file is the **source of truth** for Kutby performance expectations.
- Foundation architecture (Module Manager, Asset Manifest Manager, Tokens, Field Registry, Admin Components) must be used in ways that **preserve** these rules — never to bypass them.
- New documentation or standards may extend this file; they must not weaken it without an explicit revision note at the top.

**Revision:** 1.1  
**Effective:** Foundation 2.5 stable / frontend-ready era  

Kutby should feel extremely fast. Treat every exception as debt.

---

## 21. Performance First

Performance is a product feature.

Users notice speed before they notice new functionality.

Any feature that provides little user value while introducing measurable performance cost should be reconsidered.

Speed is not a phase after Foundation. Speed is how Kutby ships.
