17 — Production Readiness & Launch Checklist
Status: 🟡 Live checklist. The frontend-phase items are done and ticked here with the evidence; everything else is gated on the backend phase (16) or on decisions only the business can make. Re-read this before any deploy that a real restaurant will touch.
🟢 বাংলা ভূমিকা: প্রোডাকশনে যাওয়ার আগে কী কী লাগবে — কোনটা এখন শেষ, কোনটা ব্যাকএন্ড আসার পরে, আর কোনটা ব্যবসার সিদ্ধান্ত। প্রতিটা ✅-এর পাশে প্রমাণ দেওয়া আছে, যাতে "হয়ে গেছে" কথাটা যাচাই করা যায়।
1. Two different launches
Confusing these is the main way a date slips.
| Launch | What it needs | Status |
|---|---|---|
A — The marketing site (restaurant-os.com, the landing page, pricing, about, contact, legal, docs) | Real legal text, a contact address that is monitored, DNS, analytics | Buildable today; only §5 items block it |
| B — A real restaurant taking real orders | Everything in A, plus the whole backend phase: database, auth, live orders, payments, backups | Blocked on 16 |
Nothing in the product should be sold as B until B's checklist passes. A demo tenant on the live domain is fine; a restaurant whose evening depends on it is not — today's "persistence" is one browser's localStorage (CLAUDE.md rule 1).
🟢 বাংলা সারাংশ: দুটো আলাদা লঞ্চ — (ক) মার্কেটিং সাইট, যেটা আজই দেওয়া যায় (শুধু আইনি টেক্সট, ডোমেইন, অ্যানালিটিক্স বাকি), আর (খ) আসল রেস্টুরেন্ট আসল অর্ডার নেবে — সেটার জন্য পুরো ব্যাকএন্ড দরকার। আজকের "ডেটা" শুধু একটা ব্রাউজারের localStorage-এ থাকে, তাই (খ)-এর প্রতিশ্রুতি এখন দেওয়া যাবে না।
2. Done — with evidence
| Area | Item | Evidence |
|---|---|---|
| Security | HSTS, nosniff, Referrer-Policy, X-Frame-Options, Permissions-Policy, no X-Powered-By | next.config.ts; curl -I on a built server shows all six |
| Security | Staff surfaces noindex + disallowed in robots.txt | src/app/robots.ts, docs/02 §page metadata |
| SEO | Sitemap covering the marketing site and every active restaurant's menu, table picker and live campaigns, under the restaurant's own domain | src/app/sitemap.ts |
| SEO | Restaurant + full Menu structured data on every menu; Offer on every campaign; Organization + WebSite on the landing page | src/lib/structured-data.ts, 8 unit tests |
| SEO | Canonical URL per public page (a restaurant's own domain wins), hreflang between en and bn on the landing page | curl shows <link rel="canonical"> / alternate |
| Sharing | Generated share cards for the menu, each campaign and the landing page — restaurant's name, tagline and accent, Bengali glyphs included | opengraph-image.tsx ×3, src/lib/og.ts; cards render at 1200×630 |
| A11y | Skip-to-content link on every surface, <main> landmark on each | src/components/shared/skip-to-content.tsx; MAIN_CONTENT_ID on 11 surfaces |
| A11y | Reduced-motion respected; focus-visible rings; aria-labels and placeholders translated | globals.css, docs/11 |
| UX | Loading skeletons on all eight guest/staff/owner routes that fetch | loading.tsx ×8 |
| UX | Kitchen can 86 a dish and the next scan does not show it; guests rate a meal and the owner reads it; A5 bilingual table tent card | docs/06 §2.1, docs/05 §7, docs/04 §5 |
| UX | Dish names in Bengali as well as English; tips at checkout, itemised and carried onto the receipt | docs/04 §3.2, docs/05 §5 |
| UX | Offline notice + service worker; printable receipts; empty states on every board | sw.js, print-receipt.ts, EmptyState |
| i18n | 1815 keys at parity, no hardcoded guest- or dashboard-facing text | parity check + string scan (5 known exceptions: CNAME, a T-13 placeholder, the deliberately bilingual global-error, two platform-admin lines) |
| Quality | 361 unit tests, 226 E2E journeys, full CI gate on every push | .github/workflows/ci.yml |
🟢 বাংলা সারাংশ: এই টেবিলটাই "কী কী শেষ" তার প্রমাণসহ তালিকা — সিকিউরিটি হেডার, staff পেজ noindex, sitemap, schema.org স্ট্রাকচার্ড ডেটা, প্রতিটা পাবলিক পেজের canonical ও hreflang, শেয়ার কার্ড (বাংলা অক্ষরসহ), skip-to-content ও <main> ল্যান্ডমার্ক, আটটা রুটে লোডিং স্কেলিটন, অফলাইন নোটিস, ১৫৩৪ কী-তে দুই ভাষার সমতা, ৩৩৭ ইউনিট ও ২০৭ E2E টেস্ট।
3. Blocked on the backend phase
Not gaps in the design — they cannot exist without a server. Each links to where it is planned.
- Real auth.
SessionGuardis a client-side prop and is deleted in phase 2 (16 §2). - Data that survives a device. Every owner edit is
localStoragetoday (16 §3.1). - Content-Security-Policy with a nonce — needs the proxy to generate one per request (12 §6).
- Rate limiting on order submit and PIN sign-in (12 §3).
- Payments, SMS, invoices (08).
- Backups and point-in-time recovery (14 §1).
- Custom domains issuing real certificates (13 §8).
🟢 বাংলা সারাংশ: এগুলো ডিজাইনের ঘাটতি না — সার্ভার ছাড়া সম্ভবই না: আসল লগইন, ডিভাইস বদলালেও টিকে থাকা ডেটা, nonce-সহ CSP, রেট লিমিট, পেমেন্ট ও SMS, ব্যাকআপ, আর কাস্টম ডোমেইনে আসল সার্টিফিকেট।
4. Before the first deploy — operational
| # | Item | Why it is not optional |
|---|---|---|
| 1 | Error tracking (Sentry or equivalent), server + browser | Today a guest hitting an error sees a friendly screen and nobody is told. A restaurant cannot report what it did not see |
| 2 | Uptime check on the marketing page and one tenant menu, alerting a phone | A menu that is down during dinner service is the whole product being down |
| 3 | Analytics (Vercel Analytics or Plausible — cookie-free keeps §6 simple) | Otherwise there is no way to tell whether scans convert to orders |
| 4 | Log drain + CRON_SECRET-protected jobs | Cron routes are public URLs; unprotected they are a free way to run your jobs |
| 5 | A staging environment that mirrors production | Migrations must fail somewhere that is not dinner service |
| 6 | Deploy protection on production (required checks, no direct pushes to main) | CI already runs the full gate; nothing should bypass it |
| 7 | A rollback you have actually practised | Vercel instant rollback covers code; a database migration needs its own down path |
🟢 বাংলা সারাংশ: প্রথম ডিপ্লয়ের আগে অপারেশনাল কাজ: এরর ট্র্যাকিং (নইলে ভুল হলে কেউ জানবেই না), আপটাইম মনিটর (ডিনারের সময় মেনু বন্ধ মানে পুরো প্রোডাক্ট বন্ধ), অ্যানালিটিক্স, cron সুরক্ষা, স্টেজিং এনভায়রনমেন্ট, main-এ সরাসরি push বন্ধ, আর অন্তত একবার অনুশীলন করা rollback।
5. Before the first deploy — content and legal
| # | Item | Where |
|---|---|---|
| 1 | Replace the template legal text. The pages say on their face that they are templates | /legal/{privacy,terms,cookies} |
| 2 | A privacy policy that matches reality: guest phone numbers, how long they are kept, who processes payments | decided in 16 §4 |
| 3 | Monitored addresses behind sales@ / support@ / privacy@ | src/config/site.ts |
| 4 | Real social handles, or remove them from the footer | src/config/site.ts → social |
| 5 | A cookie banner only if you add tracking cookies. Cookie-free analytics avoids it; the cookies page must then say so honestly | §4 item 3 |
| 6 | Pricing that matches what you will charge, including VAT treatment in Bangladesh | subscriptionPlans |
🟢 বাংলা সারাংশ: কনটেন্ট ও আইনি কাজ: টেমপ্লেট আইনি টেক্সট বদলানো (পেজেই লেখা আছে যে এগুলো টেমপ্লেট), বাস্তবের সাথে মিলিয়ে প্রাইভেসি পলিসি, ইমেইল ঠিকানাগুলো সত্যিই মনিটর করা, ফুটারের সোশ্যাল লিংক আসল করা বা বাদ দেওয়া, ট্র্যাকিং কুকি থাকলে তবেই কুকি ব্যানার, আর VAT-সহ আসল দাম।
6. Design QA — run before each release
Ten minutes, and it catches most of what users report. Run at 375 px and at desktop, in both themes and both languages.
- Every screen at 375 px — no horizontal scroll. (
document.documentElement.scrollWidth <= innerWidthin the console.) - Bengali at every breakpoint — Bengali runs longer than English; buttons and tabs are where it breaks first.
- Dark and light on the guest menu, dashboard and promo page — including images and badges over photos.
- Keyboard only through the order flow: Tab from the top, skip link, menu, cart, place order.
- Tab titles and share cards on the pages you changed.
- Slow 3G + offline on the guest menu: skeletons appear, the offline notice appears, nothing white-screens.
- A long restaurant name and a long dish name — the two strings that break layouts.
- Print the receipt from the orders board.
🟢 বাংলা সারাংশ: প্রতিটা রিলিজের আগে দশ মিনিটের ডিজাইন যাচাই: ৩৭৫ পিক্সেলে কোনো পেজ আড়াআড়ি স্ক্রল করে কিনা, বাংলায় লেখা লম্বা হয়ে বাটন ভাঙে কিনা, ডার্ক ও লাইট দুটোতেই ঠিক দেখায় কিনা, শুধু কি-বোর্ড দিয়ে অর্ডার করা যায় কিনা, ট্যাব টাইটেল ও শেয়ার কার্ড ঠিক আছে কিনা, ধীর নেটে ও অফলাইনে কী হয়, লম্বা নামে লেআউট ভাঙে কিনা, আর রসিদ প্রিন্ট হয় কিনা।
7. Performance targets
Measured on a mid-range Android on 4G, because that is what a guest at a table is holding — not a laptop on office wifi.
| Metric | Target | Why this number |
|---|---|---|
LCP on /table | < 2.5 s | The menu is the product; a guest has already waited for the scan |
| INP | < 200 ms | Tapping a dish must feel instant on a phone |
| CLS | < 0.1 | A shifting menu makes people tap the wrong dish |
JS on /table | < 200 KB gzipped — not met: 340 KB (see below) | Every extra KB is a second on a crowded restaurant's wifi |
Where /table's 340 KB goes (gzipped, the scripts a modern browser downloads for the first paint — noModule polyfills excluded, measured against next start): about 150 KB is React 19 and the Next.js runtime, which no page of this app can go below; 44 KB is framer-motion; the rest is the menu itself. Getting under 200 KB would mean taking framer-motion off the guest surface, which is a design decision rather than a tidy-up. The target stays as a direction, not a gate.
What was fixed on the way (360 → 340 KB): the table page imported MenuExperience through the folder's index.ts, and a server page importing a barrel of client components makes every export a client reference — so sheets that load on demand were shipped up front anyway. Pages now import components directly. The guest's cart and menu also stopped reading mock-data in the browser (every seeded restaurant's staff, orders and promos); the page resolves this restaurant's slice on the server (lib/guest-context.ts). Item detail, allergy filter, reservations, package detail, split bill, spin wheel and the photo lightbox load when first opened and are prefetched when the page is idle.
To measure again: build, next start, then sum the gzipped size of every /_next/static/chunks/*.js the page's HTML references, skipping nomodule tags.
mermaid (~1 MB) is loaded only by /docs, never by a guest or dashboard page — keep it that way when adding libraries: check which route the bundle lands in.
🟢 বাংলা সারাংশ: পারফরম্যান্স মাপতে হবে মাঝারি অ্যান্ড্রয়েড ফোনে 4G-তে — কারণ টেবিলে বসা অতিথির হাতে সেটাই থাকে, ল্যাপটপ না। লক্ষ্য: /table-এ LCP ২.৫ সেকেন্ডের কম, INP ২০০ মিলিসেকেন্ডের কম, CLS ০.১-এর কম, আর JS ২০০ KB-এর কম। mermaid শুধু /docs-এ লোড হয় — নতুন লাইব্রেরি যোগ করার সময় দেখে নিতে হবে সেটা কোন রুটের বান্ডলে যাচ্ছে।