Skip to content

Interview & Self-Check Question Bank

A bank of interview and self-check questions for uEngage Prism (the F&B CRM: uengage-crm monolith + prism-services serverless). Questions mix conceptual ("why is it built this way?") with code-specific ("which cron drains which queue?"). Every question has an Answer you can reveal.

Use this for: interviewing candidates, onboarding self-tests, or as a companion to the New-Joiner Learning Path. For graded, level-structured exams see 15-Assessments.

How to use this bank

  • Interviewers: pick 2–3 conceptual + 2–3 code-specific per area. Follow-ups matter more than the first answer.
  • New joiners: cover the answer, attempt aloud, then reveal. Track which areas you miss and re-read the linked KB page.
  • Difficulty tags: 🟢 foundational · 🟡 working knowledge · 🔴 deep/senior.

Index

Area Focus
1. Product & Business What Prism does, revenue model, RFM, journeys
2. Architecture Monolith vs serverless, Kafka, crm_queue seam, dual stores
3. Data & Datastores MongoDB/MySQL split, keys, indexes, tech debt
4. Queues & Crons Queue collections, cron catalog, overlap protection
5. Security & Auth 6 middlewares, hardcoded secrets, CORS
6. Per-Module Order Ingestion, CRM, Campaigns, Loyalty, RFM, WhatsApp, Dashboard, Feedback
7. Rapid-fire code-specific Name-the-file / name-the-cron drills

1. Product & Business

Q1.1 🟢 In one sentence, what does Prism do and who buys it?

Answer Prism is uEngage's B2B SaaS **restaurant/F&B customer-engagement & retention platform**, sold to restaurant *brands* (the client); the brand's diners are the *customers/guests* whose data it manages. It ingests every order/customer across channels, builds Customer 360 / Order 360, segments with RFM, and drives repeat business via loyalty, memberships, referrals, journeys and campaigns. See [Product-Overview](../01-Business/Product-Overview.md).

Q1.2 🟢 Name the three layers of the Prism value proposition.

Answer (1) **Ingest & Unify** — pull every order+customer into one profile; (2) **Understand** — Customer 360, Order 360, RFM segments, 37 filters; (3) **Act & Retain** — campaigns, journeys, loyalty, membership, referral, feedback. Motto: *"filter to action in seconds."*

Q1.3 🟡 What are Prism's revenue streams?

Answer SaaS subscription (Basic Free / Premium ₹12,000/yr / Business custom), Digital Bills (per-bill), Feedback comms, WhatsApp (WABA setup fee + per-message markup), SMS (per-SMS). See [Product-Overview §2](../01-Business/Product-Overview.md).

Q1.4 🟡 Explain RFM. What does "relative to the brand's own base" mean, and what score does a never-ordered customer get?

Answer RFM = **R**ecency, **F**requency, **M**onetary, each scored 1–5 **by quintile of that brand's own customer base** (not an absolute threshold) — top 20% most-recent get R=5, etc. A never-ordered customer is **R=1**. `FM = Ceil((F+M)/2)` collapses the grid; an R×FM lookup assigns one of **11 segment labels**. See [Terminologies](../01-Business/Terminologies.md).

Q1.5 🔴 Name at least 5 of the 8 automated journeys and the general shape a journey follows.

Answer Shape: **EVENT → CONDITION → ACTION**. The 8: Welcome (sign-up), Birthday, Anniversary, Wallet-points-near-expiry, Lost Customers (no order for X days), Abandoned Cart, Low-Rating Rewards, Referral Boost. On activation up to **10,000** past customers are backtracked. See [Business-Flow §4](../01-Business/Business-Flow.md).

Q1.6 🟡 What are the cross-widget data-integrity rules that must always hold, and why do they matter?

Answer `Total Customers = Transacting + Non-Transacting`; `Total Orders = Delivery + Pick-Up + Dine-In`; `Total Loyalty Points = Used + Available + Expired` (never negative); revenue-by-channel sums to 100%; `Unique Customers ≤ Redemptions`. They are the **P0 acceptance bar** for any dashboard work (Data Validation Protocol A4). Known violations are the "E" bugs (e.g. E1.10). See [Roadmap §E](../01-Business/Roadmap.md).

Q1.7 🔴 What happens to a customer's unredeemed points on tier downgrade, and why is that worth knowing?

Answer On downgrade, **all unredeemed points are immediately forfeited** (Loyalty rule 2.3, strict). It produces "the system stole my points" tickets and interacts with the E1.10 points-conservation bug. Downgrade runs nightly via `crons/loyalty_milestone_downgrade.js`. See [Business-Flow](../01-Business/Business-Flow.md).

2. Architecture

Q2.1 🟢 Prism is "two deployables, one database." Name them and what each does.

Answer **uengage-crm** — long-running Express monolith (PM2): APIs, dashboards, campaigns, loyalty, and 91 in-process crons. **prism-services** — AWS Lambda + Kafka (MSK) serverless tier absorbing high-volume POS ingestion. Both write to the same MongoDB `uengage` DB; the **`crm_queue` collection is the seam** between them. See [System-Overview](../00-Getting-Started/System-Overview.md).

Q2.2 🟡 Why was the serverless tier added, and what does Kafka buy you?

Answer Prism began as a single monolith doing everything. As order volume grew, a serverless ingestion tier was placed in front to **absorb spiky, high-throughput POS traffic without risking the monolith**. Kafka (MSK topic `prism-ingest-orders`) **decouples** the burst of incoming orders from slower downstream processing. The system is deliberately **mid-migration**. See [High-Level-Architecture §2](../02-Architecture/High-Level-Architecture.md).

Q2.3 🟡 What is the "dual-writer" problem on crm_queue?

Answer Two independent code paths write to `crm_queue`: the `orders-consumer` Lambda **and** the monolith's own ingestion controllers (`uengageOrderIngestion.js`, `petpoojaOrderIngestion.js`, etc.). There is no dedup/idempotency between them → risk of **duplicate orders and races**; unclear source-of-truth. See [High-Level-Architecture §9](../02-Architecture/High-Level-Architecture.md) and map-05.

Q2.4 🟡 Name the 3 Kafka topics and their consumers. Which are stubbed?

Answer `prism-ingest-orders` → `orders-consumer` λ (writes `crm_queue`, batch 10). `whatsapp-messages` → `consumer-send` λ (**stubbed, log-only**). `whatsapp-receipts` → `consumer-receipts` λ (**stubbed, log-only**). The monolith's own WhatsApp crons currently do the real send/receipt work; reconciling this is a roadmap item. See [High-Level-Architecture §4](../02-Architecture/High-Level-Architecture.md).

Q2.5 🔴 Data plane vs control plane: walk through the five planes.

Answer **Ingestion** (orders/customers → `crm_queue`, both Lambda + monolith); **Processing** (drain queue → Customer 360/Order data, points, journeys — monolith crons + commonFunctions); **Intelligence** (RFM, segments, MIS — crons + processLibrary); **Activation** (campaigns/journeys → WhatsApp/SMS/Push — channel queues + crons); **Presentation** (dashboards, reports, `/stats`). See [High-Level-Architecture §3](../02-Architecture/High-Level-Architecture.md).

Q2.6 🔴 Why do "two dashboards" and "three campaign pipelines" both exist? What does that tell you?

Answer It's the visible surface of an in-progress Next.js migration. Dashboards: `dashboardController.js` (legacy) + `dashboardControllerNew.js` (unified, Redis-cached, Socket.io). Campaigns: `campaignController.js` (traditional SMS/FCM/Email/WhatsApp) + `campaignControllerDynamic.js` (custom-code) + `automatedCampaignController.js` (journeys). Lesson: when in doubt, check which pipeline a feature actually routes through before editing. See map-02 and [Module-Architecture](../02-Architecture/Module-Architecture.md).

Q2.7 🔴 Where are the non-atomic points in the system, and what failure mode do they create?

Answer Loyalty points span **MySQL** (`wallet`, `wallet_history`) and **MongoDB** (`orders`, `customers`, `wallet_transactions`) with **no distributed transaction**. A crash mid-flow can update the wallet but not mark the order, or vice-versa → **balance drift** (the E1.10 2-point discrepancy class of bug). Mitigation is idempotency (`order.points_allocated` flag) + a reconciliation job, not true atomicity. See map-04 §5.7.

Q2.8 🟡 Why do crons "in the web process" matter operationally?

Answer All **91 crons are `require()`d at startup in `index.js`** and run inside the same Node process as the web server, so a heavy cron can **couple cron load to API latency**. That's precisely why MySQL has two pools — `webPool` (15) for requests and `jobsPool` (10) for crons — so background jobs never starve web traffic. See [System-Overview §3](../00-Getting-Started/System-Overview.md).

3. Data & Datastores

Q3.1 🟢 What lives in MongoDB vs MySQL? Give the rule of thumb.

Answer **MongoDB `uengage`** (maxPool 400, SECONDARY_PREFERRED): high-write queues (`crm_queue`, ingestion dumps), campaign MIS, journeys, ratings/NPS, dashboard MIS, socket chat, config. **MySQL `addo_*`** (webPool 15 + jobsPool 10): transactional/joined data — orders, order items, users/roles/auth, customers, `wallet`/`wallet_history`/`wallet_rules`, `crm_points_allocation_queue`, promo engine, menus. Rule of thumb: **Mongo = fast-changing/analytical buffers, MySQL = money & identity.** See [High-Level-Architecture §5](../02-Architecture/High-Level-Architecture.md).

Q3.2 🟢 What is the universal customer join key, and the tenant scope?

Answer Customer key = the **10-digit Indian mobile number** (no country code) — the join key across MongoDB *and* MySQL. Tenant scope = **`parent_business_id` (brand/HQ) + `child_business_id` (outlet)** — nearly every query filters on these. See [System-Overview §6](../00-Getting-Started/System-Overview.md).

Q3.3 🟡 Trace the linking chain from a mobile number to a wallet balance.

Answer `mobile → addo_contacts.mobileNo → addo_contacts_mapping.contactId → wallet.contactMappingId → loyalty/transaction tables`. Business isolation via `parent_business_id`/`child_business_id`. See map-03.

Q3.4 🔴 Name two concrete indexing gaps and the query each would speed up.

Answer MySQL: `wallet (parentBusinessId, validTillDate, id)` — speeds **campaign audience/segment extraction** (`WHERE parentBusinessId=? AND validTillDate>=TODAY()`); `addo_orders (businessId, orderDate)` — speeds **daily aggregations**. MongoDB is missing indexes on `parent_business_id`/`business_id` (every aggregation) and `created_at` (MIS time-range). See map-03 "Indexing Gaps".

Q3.5 🔴 Give two data-consistency tech-debt items in the data layer.

Answer (1) **No TTL cleanup** on `crm_queue` and `*_dump` collections → unbounded growth. (2) **Business-ID type inconsistency** — sometimes String, sometimes Number across MongoDB, which breaks naive equality filters. Also: soft-deletes only (no `deleted_at`), undocumented denormalized `mobileNo` sync. See map-03 "Tech Debt Notes".

4. Queues & Crons

Q4.1 🟢 Name the four channel/work queues and their status fields.

Answer SMS `sms_queue_YYYYMMDD` (`processed` bool); Push `push_noti_queue_YYYYMMDD` (`processed` bool); WhatsApp `whatsapp_queue_YYYYMMDD` (`process_status` int: 0 pending, 2 picked, 3 sent/pending-webhook, 4 skipped); Points `crm_points_allocation_queue` (MySQL, `status` string: pending/done/failed). The inconsistent status conventions are themselves tech debt. See [Queues](../06-Queues/README.md), map-04 §4.1.

Q4.2 🟡 Which cron drains crm_points_allocation_queue, how often, and what does it do on failure?

Answer `crmPointsAllocationQueueCron.js`, **every 2 min**, batch ~5000 (`status='pending'`). Types: `uen_order`, `order_reward`, `welcome_point`, `refer_earn`. Calls `handleWalletPointsFlow` or `helpers.handleOrderRewards`. On failure it **retries max 5×** with ~15-min backoff (`next_retry_at`). See map-04 §1.

Q4.3 🟡 Why are there two WhatsApp send crons with different cadences?

Answer Traffic is split by the `type` field on the queue doc. **Marketing** (no `type`) → `sendWhatsappMessageCron.js` (~every 3 s). **Transactional/order-related** (`type` present) → `sendWhatsappTransactionalCron.js` (~every 1 s) so time-sensitive order messages jump the queue. Both poll `process_status=0` and delegate to `whatsappMsgProcess.js` → Gupshup/Meta. See map-04 §1.

Q4.4 🟡 Which cron computes RFM, when, and where does it write?

Answer `crons/rfmSegments.js`, **3 AM daily** (`0 3 * * *`), over a **90-day window** per parent business. Assigns segments (new_customers, promising, regular, champions, need_attention, at_risk, lost) and writes to `rfm_metrics_tmp` + `rfm_job_log`. See map-04 §1.

Q4.5 🟡 Which cron deletes old crm_queue records and on what rule?

Answer `deleteCrmQueue.js`, every 2 days (`0 2 */2 * *`). Deletes `status=1` (completed) records **and** all `source=6` non-terminal events (keeping only "Closed"). This is the workaround for the missing TTL. See map-04 §1.

Q4.6 🔴 How does cron overlap protection work today, and where does it fall short?

Answer Crons check a `status:false` lock in a `*_cron_status` / `cron_stats` document before running; if the previous run isn't marked `status:true`, the new run **skips**. Stuck detection is reactive (~20 min → alert email), with a force-close fallback (~4 h, seen in `dedupCustomers`). It falls short: **no automatic recovery / circuit breaker**, and locks are per-document not distributed. See map-04 §5.6, [Request-LifeCycle §4](../02-Architecture/Request-LifeCycle.md).

Q4.7 🔴 Two loyalty-points queues coexist. Which is legacy and why does it matter?

Answer **Legacy:** MongoDB `loyalty_points_allocate` drained by `pointAllocation.js` (~every 1 min, status 0/1/-1). **Current:** MySQL `crm_points_allocation_queue` drained by `crmPointsAllocationQueueCron.js` (~2 min, with retry/backoff). Matters because points can be produced/consumed by *either* path — a debugging trap if you check only one. See map-04 §5.2.

Q4.8 🟡 Roughly how many crons exist, and how are they categorized?

Answer ~91 crons (map-01/map-04 count ~93 files incl. subdirs: autoCampaignRoiCrons, rewardsCrons, updationCrons). ~14 are "critical" (ingestion, points, channel sends, RFM, downgrade, dedup, cleanup); the rest are reporting/cleanup/auxiliary. See map-04 §7.

5. Security & Auth

Q5.1 🟢 Name the six auth middlewares and who each is for.

Answer `authMiddleware` (business/staff, MySQL token/`auth_tokens`/bcrypt); `adminMiddleware` (admins, `business_id==1`); `appMiddleware` (customer loyalty app, Mongo `addo_contacts_mapping` token); `jwtMiddleware`/`jwtWhatsappMiddleware` (internal/WhatsApp, JWT HS256 with hardcoded secrets ⚠️); `crm_offer_middleware` (POS/3rd-party, Mongo `authorization_token`); `injestion_middleware`/`thirdPartyMiddleware` (ingestion, hardcoded static token ⚠️). See [System-Overview §7](../00-Getting-Started/System-Overview.md), [Authentication](../08-Security/Authentication.md).

Q5.2 🟡 What order do the global middlewares run in, and what does the traceId middleware give you?

Answer `bodyParser` (urlencoded+json) → `cors()` (**wildcard** ⚠️) → `traceIdMiddleware` → `requestLogger` → route + route-level auth. `traceIdMiddleware` sets a request-scoped `trace_id` (from `x-trace-id` or new UUIDv4) via AsyncLocalStorage, so you can **grep logs by `trace_id`** to reconstruct a request. See [Request-LifeCycle §1](../02-Architecture/Request-LifeCycle.md).

Q5.3 🔴 List the headline security debts a new joiner must not repeat.

Answer Hardcoded JWT secrets and static ingestion tokens; **wildcard CORS**; **no rate limiting**; committed `.env.dev`; plaintext MongoDB Atlas URI + hardcoded MSK brokers in the `serverless-*.yml` files; hardcoded Redis ElastiCache host in `utilities/redis.js`. Auth failures are at least **fail-closed** (403/500). See [Security](../08-Security/README.md), map-05 §10.

Q5.4 🟡 How does requestLogger handle a request carrying a plaintext password?

Answer It **flags/logs the metadata but never logs the value** — a security monitor for accidental plaintext credentials. See [Request-LifeCycle §1](../02-Architecture/Request-LifeCycle.md).

6. Per-Module

Order Ingestion

Q6.1 🟡 Trace an order from POST to processed. Which collection, which status values?

Answer `POST /crm_api/injestion/{pos}` (Lambda `orders-api`) **or** monolith `/injestion/{pos}` → insert into **`crm_queue`** `{data, source:, status:0, insertedAt, apiSource}`. Ingestion crons poll `status:0`, upsert customer + order (recompute LTV/LTO/AOV), enqueue points, trigger digital bill + journeys, then mark processed. Source ids: 1=uengage, 2=petpooja, 3=ls_center, 4=orderPush, 6=rista, 7=posist. See [Order-Ingestion](../03-Modules/Order-Ingestion/README.md), map-05.

Q6.2 🟡 Which controllers/crons ingest orders, and what's the smell?

Answer `commonOrderIngestion.js` (~2 min), `petpoojaOrderIngestion.js` / `ristaOrderIngestion.js` / `uengageOrderIngestion.js` (~1 min), `shoutloOrderIngestion.js` (~2 min); HTTP gateway `injestionController.js` (`crmQueue()`, `petPooja()`, `rista()`, `posist()`, `ls_center()`). Smell: 5 near-duplicate crons for 5 POS types + a parallel Lambda path — duplicated error/email logic. See map-02.

Customer / CRM

Q6.3 🟡 What's the "largest and most central" controller, and name two responsibilities.

Answer `customerController.js` (40+ functions): customer CRUD, segmentation, RFM lookups, loyalty/rewards, chat integration, occasional rewards (`checkOccasionalRewards`), feedback loyalty points (`feedbackLoyaltyPoints`). App-side lightweight creation is split into `appCustomerController.js` (writes dumps for async ingestion). See map-02.

Q6.4 🔴 How is customer identity unified across channels, and which cron cleans duplicates?

Answer By **phone number**. `crons/dedupCustomers.js` (1 AM daily) dedups the `customers` collection by `mobileNo`, picks canonical by (LTO desc, LTV desc, `_id`), re-points non-canonical outlets to canonical, deletes duplicates. Dedup correctness is called out as critical (Dashboard 3.3). See map-04, [Business-Flow §2](../01-Business/Business-Flow.md).

Campaigns

Q6.5 🟡 Walk the campaign flow and say where the business wallet is debited.

Answer Select channel → audience (segment/upload/cohort) → template → sender ID + ROI window → schedule → send to channel queue → delivery/read/click tracking → ROI attribution (revenue in the attribution window) → MIS. The **business wallet is debited per message** (in `sendSms.js` / campaign builders) and sends are **blocked if balance is insufficient**. See [Business-Flow §5](../01-Business/Business-Flow.md), [Campaigns](../03-Modules/Campaigns/README.md).

Q6.6 🟡 What is ROI attribution and where is it stored?

Answer Revenue credited to a campaign for orders placed within a **configurable attribution window (hours)** after the send. Tracked in `auto_campaign_roi` / `campaign_mis` via `processLibrary/campaignMis.js` and `uengageProcess*ROI*` helpers. See [Terminologies](../01-Business/Terminologies.md).

Loyalty

Q6.7 🟡 What is the core points-allocation function and what does it touch?

Answer `commonFunctions/handleWalletPointsFlow.js` — checks fraud rules + eligibility, inserts wallet transactions, updates MySQL wallet balance, and triggers milestone upgrade (`handleMilestoneUpgrade.js`). Reward amount comes from `rewardHelper.js`. Logic is spread across these + `helpers.js` (a consolidation target). See map-04 §2, §5.8.

Q6.8 🔴 A brand configures different earn % for Dine-in vs Delivery. Where does that live and what rule complicates the ledger?

Answer Per-order-type earn/burn % lives in loyalty config (`wallet_rules`); applied during allocation. Complication: `Total = Used + Available + Expired` must always hold and never go negative, but tier **downgrade forfeits unredeemed points** and non-atomic Mongo/MySQL writes can drift it (E1.10). See [Loyalty](../03-Modules/Loyalty/README.md), [Roadmap §E](../01-Business/Roadmap.md).

RFM / Intelligence

Q6.9 🔴 Distinguish rfmSegments.js from customerInsightsCron.js / segments_count.js.

Answer `rfmSegments.js` (3 AM) = pure R/F/M scoring → 11 segments → `rfm_metrics_tmp`. `customerInsightsCron.js` = advanced multi-filter segmentation (mobile validity, visited_days, time_of_day, order_amount) → `segment_counts`. `segments_count.js` (4:45 PM) = simple reachability counts (SMS dnd=0, WhatsApp optin=1, Push has FCM). See map-04 §1.

WhatsApp

Q6.10 🔴 Where does the "WhatsApp volume mismatch" (E1.3) come from architecturally?

Answer Sends and receipts flow through **two half-migrated paths**: the monolith's WhatsApp queue/crons do the real work, while the Kafka `whatsapp-messages`/`whatsapp-receipts` consumers are **stubbed (log-only)** — so delivery receipts (`process_status=3` "pending webhook") aren't fully reconciled and there's no visible delivery-receipt processor cron. Counting from an incomplete pipeline yields inaccurate volume reports. See [High-Level-Architecture §7](../02-Architecture/High-Level-Architecture.md), map-04 §5.11.

Dashboard / Analytics

Q6.11 🟡 Which module builds the daily aggregates, and name three collections it writes.

Answer `processLibrary/aggregationProcess.js` (ETL, IST date math) builds ~14 collections, e.g. `orders_by_day_business`, `fulfillment_breakdown_by_parent`, `daily_summary`. `dashboardMIS.js` writes `dashboard_mis` (orders, revenue, AOV, customer counts, sentiment). See map-04 §3.

Feedback / NPS

Q6.12 🟡 How does a low rating get handled differently from a high one?

Answer High rating → routed to Google/Zomato public reviews. Low rating → triggers the **low-rating recovery journey + promo** (`lowRatingTrigger.js`, FCM/JWT). NPS buckets responses Promoter (9–10)/Passive (7–8)/Detractor (0–6). See [Business-Flow §7](../01-Business/Business-Flow.md), map-02.

7. Rapid-fire code-specific

Cover the answers; aim for instant recall.

# Question Answer
R1 Cron that drains crm_points_allocation_queue? cadence? crmPointsAllocationQueueCron.js, ~2 min
R2 Cron that computes RFM? when? rfmSegments.js, 3 AM
R3 Cron that downgrades loyalty tiers? loyalty_milestone_downgrade.js, 2 AM
R4 Cron that dedups customers? dedupCustomers.js, 1 AM
R5 Cron that cleans crm_queue? deleteCrmQueue.js, every 2 days
R6 Marketing vs transactional WhatsApp cron? sendWhatsappMessageCron.js (3 s) / sendWhatsappTransactionalCron.js (1 s)
R7 SMS / Push send crons? smsQueueCron.js (3 min) / pushQueueCron.js (5 min)
R8 Core points-allocation function? commonFunctions/handleWalletPointsFlow.js
R9 Main journey/campaign orchestrator (processLibrary)? uengageProcess.js (~89KB)
R10 Collection that is the monolith↔serverless seam? crm_queue
R11 Kafka topic for orders? prism-ingest-orders
R12 Source ids 1/2/6/7? uengage / petpooja / rista / posist
R13 MongoDB pool size + read pref? maxPool 400, SECONDARY_PREFERRED
R14 MySQL two pools + sizes? webPool 15, jobsPool 10
R15 Legacy vs new dashboard controllers? dashboardController.js / dashboardControllerNew.js
R16 AOV formula (and E1.9 bug)? Revenue / Orders; E1.9 shows ₹0 in "Your Effective Offers"
R17 FM score formula? Ceil((F+M)/2)
R18 Aggregation ETL module? processLibrary/aggregationProcess.js