How to use this page
Open one question. Read STAR. Expand follow-ups as if the interviewer did not let you stop. Lanes keep you from claiming circuit breakers or Datadog you did not ship. Percents: mechanism, PDF number, no invented protocol.
Same-hour LP spread: do not spend Ownership, Frugality, and Dive Deep as three speeches plus Customer and Deliver on ConvBI. Primary three first; backups only if they already heard GiftedBooks / Horizon / IQVIA / Argus.
1. Project overview
1. Project overview — 90 seconds
Situation
At Ylogx, November 2024 to October 2025, I interned on a full-stack AI BI product we called ConvBI / Projas in the repo. Operators needed custom reports, a chatbot over accounting tables, and live KPIs, without one org reading another.
Task
I worked across NestJS, React, FastAPI SQL RAG, Postgres RLS, and the ECS serving path. Intern, not AWS account owner, not DBA of record.
Action — I did
- I shipped JWT → per-request SET ROLE → RLS on bot, reports, and tiles.
- I split NL to FastAPI (propose SQL) and Nest (execute as caller).
- I kept 30 dashboards on deterministic SQL at p90 under 210 ms, Redis on the bot path for −35% bot-DB, CI/CD to ECS.
Result
40% faster reports, +65% SQL RAG productivity, 3-tier RLS, −35% bot DB, 30 dashboards +60% ops, 99.9% uptime. Then I stop and let them pick a door.
If they say “is this Java?” Production intern was TypeScript and Python. Live Code is Java.
- Conv-BI GitHub name is this intern, not a second job. warpflow is adjacent, not these metrics.
2. Resume claims → interview questions
2. Resume claims → they will challenge every number
Claim: RLS + RBAC for 3 organizational tiers primary
Situation
Operators and non-SQL users query a shared warehouse. A demo that returns rows is not done if a generated JOIN can still read another org.
Task
I owned isolation on the bot and reporting path. Intern, not a named security team. Done meant the caller’s rows, including SQL the model wrote.
Action — I did
- I put identity on the request (JWT guid, role, org), then opened a per-request Postgres transaction and SET ROLE to that user’s db_role_name.
- I mapped policies → modules → tables onto the request, and I fail-closed to role rls_no_access when an admin or user had no db_role_name. An earlier RESET ROLE path leaked.
- I executed generated SELECT on that same transaction so RLS still filtered a legal JOIN. I discarded app-only WHERE org_id, Nest middleware alone, and trusting the model to add the tenant predicate.
Result
Resume: 3-tier RLS+RBAC. 99.9% serving held on the ECS path. I will not invent a leak ticket count.
- What
- Postgres RLS + RBAC on three org-shaped tiers (org / team / user-shaped — literal names are not on the resume).
- Why
- Every generated query hits Postgres. The UI never sees some JOINs. HTTP 200 with a legal JOIN is still a leak.
- How
- Per-request transaction, SET ROLE first db_role_name, GUC app.*, execute as caller, not table owner.
- Trade-off
- One active Postgres role per session — extra role names on the user are not all SET. Connection held until HTTP finish (pool max 20).
- Failure case
- Missing db_role_name without fail-closed → leak. executeWithoutRLS on warehouse tables → leak. Cache key without tier → leak.
Follow-up: Why the database, not the UI?
The chatbot and report builder both generate or compose SQL. A React hide does not run on JOIN number two. RLS is the last gate that still sees the row.
Sub: Then why FeatureAccess at all?
FeatureAccess answers “may this user open /reportdesigner.” RLS answers “which sales rows.” Route hide is not row security. ADMIN still needs assigned features in this SPA; only SUPER_ADMIN is a blanket true.
Follow-up: Why three tiers, not one admin boolean?
A boolean cannot separate org-wide finance from a salesperson’s book. Row filters and route RBAC answer different questions. I speak org / team / user-shaped levels. I do not invent labels the PDF does not print.
Follow-up: What did you personally write vs the team?
I implemented the request pipeline and the fail-closed path in this Nest tree: JWT → transaction → SET ROLE → query on that transaction. Postgres policy SQL in the warehouse, ECS account ownership, and Tally gateway internals are not a claim that I was the DBA or the AWS org owner.
Follow-up: What would you add next?
Cross-tier deny fixtures including a JOIN through a mapping table. Status-aware transaction finish. Secrets Manager. I do not hang a new percent on that list.
- 3 tiers is RLS+RBAC. Redis −35% is a different family. LangGraph is IQVIA.
-
If they ask how a percent was measured: I name the mechanism, say the PDF number, then “I do not have the study protocol on the resume.”
Claim: 99.9% uptime — they will grind this primary
Situation
The BI app had to stay up for daily queries: dashboards, reports, chatbot. A single mutable VM would have mixed deploy, health, static files, and API in one failure domain.
Task
I needed a serving path operators could keep using. Intern on the product path, not the AWS account owner.
Action — I did
- I split static JS onto CloudFront and API onto ECS tasks behind an ALB, Docker images, CI/CD rolling replacement instead of SSH-patch a box.
- I kept dashboard tiles on deterministic SQL so a model outage does not take the 30 KPIs with it.
- In this repo: GitHub Actions on qa auto-deploys; main waits three minutes for Arjun-ylogx then ECS wait-for-service-stability. Terminus /health pings Postgres.
Result
Resume: 99.9% uptime on CloudFront / ECS / Docker / ALB / Route 53 (GoDaddy registrar → Route 53). I do not invent a missed month, a Datadog board, or a p99.
- What
- Serving availability of the BI app, not the model vendor’s SLA.
- Why
- Operators open tiles every day. Coupling tiles to ChatOpenAI would make 99.9% a vendor number.
- How
- Edge static + ALB health + ECS replace + deterministic dashboards + chatbot as a separate degrade path.
- Trade-off
- No invented multi-region. One cluster name in YAML. Pool of 20 plus a held transaction per request is a concurrency ceiling.
- Failure case
- RDS down: /health fails if the load balancer uses Terminus; /health/simple can stay green. FastAPI down: tiles still work; bot returns a fallback sentence.
Follow-up: How did you calculate 99.9%?
The PDF prints 99.9% on the Ylogx serving path. I will not invent uptime = successful_requests / total, a Datadog query, or a before number. I speak the hop: CloudFront, ALB, ECS, Docker, CI/CD, and that dashboards do not wait on the model.
Sub: What was uptime before?
Not on the resume. I speak the failure class we designed against (one box, LLM on tiles, SSH deploy), not a fake baseline.
Sub: P50 or P95 of errors?
The latency family on this intern is p90 under 210 ms on dashboards. I do not invent an error p95. 99.9% is availability, not latency.
Follow-up: Your database is down for 10 minutes. How does the system behave?
Dashboard and report SQL fail. Terminus /health should go unhealthy so ALB stops sending to that task if the check includes Postgres. Chatbot SQL also fails. I do not claim a replica failover I did not ship. Would-do: multi-AZ RDS, fail the load-balancer target, show a degraded empty state, do not retry-storm the dead primary.
Sub: If every instance is unhealthy?
ALB has nowhere healthy to send. Users see 5xx or a static CloudFront shell with a failing XHR. That is the class. I did not ship a second region.
Sub: Could retries make the outage worse?
Yes. If every browser retries a dead RDS, you amplify load on recovery. Understand: exponential backoff plus jitter, retry only idempotent GETs, cap attempts. I did not ship a Resilience4j module in this Nest tree. Dashboard GETs are read-only so they are the safe class to retry with a budget. Tally upload and report POST are not.
Follow-up: Why not just add more servers?
More ECS tasks do not fix a wrong-org JOIN, a SELECT *, or an LLM on every tile. Scale the pool and the query shape first. I did not shard Postgres.
Follow-up: Monitoring, alerting, on-call?
Shipped in this tree: LoggingInterceptor (duration, status, redacted body), SQL log when SQL_LOGGING=true, slow query warn over 1s, Terminus ping. I do not claim Datadog, PagerDuty, or a named SLO dashboard. Would-do: ALB 5xx alarm, ECS running-count, RDS CPU/connections, chatbot 502 rate.
- 99.9% is Ylogx. GiftedBooks is 99.5%. IQVIA has no uptime percent.
- p90 under 210 ms is dashboards, not 99.9%.
-
If they ask how a percent was measured: I name the mechanism, say the PDF number, then “I do not have the study protocol on the resume.”
Claim: Redis −35% bot-database latency primary
Situation
Every natural-language turn that hit Postgres felt like “the model is thinking.” Repeat questions and schema lookups are not proof the RDS is undersized.
Task
Cut hot-path database work on the bot without weakening three-tier isolation, and without moving dashboard tiles onto a cache of someone else’s answers.
Action — I did
- I measured bot database latency instead of buying a larger RDS first.
- I put Redis cache-aside on repeat answers and schema metadata, with organizational tier and role in the key.
- On a miss, SQL still ran as the caller role with RLS. Redis down → Postgres as that role, never table-owner, never fail-open.
Result
Bot database latency fell 35%. Isolation stayed in Postgres. 99.9% held on the existing ECS path. No AWS bill on the PDF.
- What
- Redis cache-aside on the SQL RAG / bot database path.
- Why
- Repeats and schema lookups are cache-shaped. Instance size does not fix the same English asked twice.
- How
- GET by tier|role|fingerprint; miss → allow-listed SQL as caller role → SET the safe payload.
- Trade-off
- Stale answers if schema or policy changes. Extra moving part. A cross-tier hit is a leak, not a latency win.
- Failure case
- Redis down: fall back to Postgres as caller role. Redis up with a key that omits tier: leak. I do not invent TTL or hit ratio on the PDF.
Follow-up: Original latency? New latency? P50 or P95?
The PDF prints −35% bot-database latency. It does not print a before/after millisecond pair, p50, or p95. Dashboard family is p90 under 210 ms with no LLM. Those are two families.
Follow-up: What was the bottleneck?
Repeat NL turns re-hitting schema and hot analytics in Postgres, experienced as model delay. I discarded “the model is slow” as the only story before measuring the query path.
Follow-up: Why still RLS after a cache hit?
The key must already be isolated. A cached payload for org A must never be returned to org B because the English matched. Isolation is in the key plus executing misses as the caller role.
Follow-up: What happens if Redis goes down?
Bot path falls back to Postgres as the caller role. Latency returns. Isolation must still hold. Tiles do not depend on Redis in the resume hop — they are deterministic SQL. The in-process Map in QueryDB dying is one ECS task’s metadata cache, not the −35% claim.
Follow-up: Why not cache dashboard tiles the same way?
Tiles are already a hot, deterministic path aimed at p90 under 210 ms. Caching them is a would-do of safe aggregates. Putting Redis between orgs without a tenant key is the leak class. I did not claim −35% on dashboards.
- −35% is bot DB via Redis. p90 under 210 ms is dashboards. Map 5 min is this Nest file, not the resume Redis hop.
-
If they ask how a percent was measured: I name the mechanism, say the PDF number, then “I do not have the study protocol on the resume.”
Claim: LangChain SQL RAG, +65% data-analysis productivity backup
Situation
Non-SQL users still needed ad-hoc questions over accounting tables. Pasting 90 days of rows into a prompt truncates data and invents totals. The model cannot inherit Postgres RLS the way an executed query can.
Task
Give operators inspectable SQL and real aggregates, under the caller’s database role.
Action — I did
- I split propose vs execute: FastAPI LangChain SQLDatabase allow-list + Pydantic chart fields; Nest executeRawSelectQuery on the RLS transaction. FastAPI never sees the JWT.
- I constrained the prompt to SELECT-only, wrapped WITH as SELECT * FROM (… ) q so the Nest prefix check still holds, temperature 0.
- I kept the 30 KPI tiles on handwritten SQL so +65% lives on the chatbot path, not on dashboard generation.
Result
Resume: +65% analysis productivity. Not model accuracy. No sample size on the PDF.
Follow-up: How do you know +65%?
PDF productivity family on SQL RAG. I will not invent a diary study n. Mechanism: operators ask English, get SQL+rows instead of waiting on an analyst.
Follow-up: How does this not leak another org?
The model only proposes. Nest checks SELECT, extracts FROM/JOIN tables, hasTableAccess, optional organizationId AND, executes on SET ROLE. A JOIN that is legal SQL still loses the other org’s rows at RLS.
Follow-up: What if the SQL is wrong?
Nest returns the chatbot sentence, empty data, showChart false, error field. Tiles are unaffected. I discarded letting the model retry writes.
- Ylogx chatbot is LangChain SQL RAG, not LangGraph. +65% ≠ 40% reports.
-
If they ask how a percent was measured: I name the mechanism, say the PDF number, then “I do not have the study protocol on the resume.”
Claim: 40% faster report generation + client aggregation crash (notes) notes
Situation
Custom reports with joins, slicers, and charts. I aggregated a large slice in the browser. The UI crashed. The agent path was unstable after that slice.
Task
Keep heavy aggregation off the client when the user asks for a large slice. Isolation still in Postgres.
Action — I did
- Report builder: Nest execute-query with LIMIT 1000 default, 5000 max, join types INNER/LEFT/RIGHT/FULL, save canvas JSON via POST /reports.
- After the crash: server/preload path for the AI-requested report; explicit click to load full data; cleanup so the agent stays stable; multi-chart via the LLM path, not report-as-image.
- I discarded leaving aggregation in the client, and screenshotting a chart grid as the product.
Result
UI rendered. Agent stayed stable. Resume family on this product: 40% faster reports. That percent is not a crash-study output.
Follow-up: Did 40% come from the crash fix?
No. 40% is the report-builder product family on the PDF. The crash result is UI rendered and agent stable. I will not invent a before/after millisecond pair.
Follow-up: Why did client aggregation fail?
The browser was summing a large volume on the main thread. Memory and long tasks crash the tab. SQL already has GROUP BY. Putting that in JS does not get cheaper at 10× rows.
- Crash is notes. 40% is resume. sub-210 ms is dashboards, not this crash.
Claim: 30 KPI dashboards, +60% ops, p90 under 210 ms, React + Recharts backup
Situation
Operators needed live finance KPIs every day. Putting ChatOpenAI on each tile would invent numbers and blow the latency budget.
Task
Deterministic SQL under RLS, month/year filters, no model on that GET.
Action — I did
- I shipped fixed aggregations in DashboardsService — overview, sales, receivables, payables, collections, cashflow, stocks, P&L, balance sheet — filters via EXTRACT MONTH/YEAR.
- SPA loads JS from CloudFront, then KPI JSON from Nest on the RLS transaction. Recharts on the resume; if they open the report canvas, Chart.js ElementRenderer is also in the tree.
- I discarded one model call per tile.
Result
Resume: 30 dashboards, +60% ops, p90 under 210 ms. If they open DashboardsController: nine types, some multi-page. I speak resume 30; I name the nine if they open the registry.
Follow-up: 9 types or 30 tiles?
PDF says 30 real-time KPI dashboards. This checkout has nine controller types and multi-page sales/receivables/etc. I do not invent a tile census in the hour. Both sentences can be true: many KPI cards across nine families.
Follow-up: How did you get p90 under 210 ms?
Deterministic SQL, no LLM, pooled Postgres (max 20), CloudFront for JS so the API only returns JSON. The PDF prints p90 under 210 ms. I will not invent p50/p95, a before number, or an APM screenshot.
Follow-up: N+1 queries on a dashboard?
Overview runs several SELECTs (sales, pl, outstanding, cashflow, bs). That is a would-do to batch or materialize. I did not claim I shipped a single CTE for every tile.
- p90 under 210 ms ≠ Redis −35%. Recharts is the resume chart lib. Chart.js is in the report/chat canvas if they open GitHub.
Claim: CloudFront, ECS, Docker, CI/CD, ALB, Route 53 (GoDaddy → Route 53) backup
Situation
A single VM mixed static files, API, deploy, and health.
Task
Split edge, load balancing, and replaceable tasks.
Action — I did
- Docker images: Nest alpine USER node port 5000; frontend multi-stage nginx:80; AI gunicorn 8 workers timeout 180.
- GitHub Actions: qa branch auto ECS; main requires Arjun-ylogx in 3 minutes then wait-for-service-stability. Region ap-south-1, cluster convbi-cluster, service convbi-backend-service.
- Resume hop: CloudFront static, Route 53 after GoDaddy registrar, ALB, ECS. I did not operate EKS. Kubernetes is a skills line, not this intern’s production hop.
Result
99.9% family lives here. I do not invent desired count, Fargate vs EC2, or an AWS bill.
Follow-up: CodeBuild vs GitHub Actions?
Older latex draws CodeBuild. This checkout’s workflows are GitHub Actions. I speak the file they opened. Resume named hop is CI/CD + ECS.
Follow-up: What is in the task definition you will not recite?
cpu 1024, memory 3072, container port 5000. JWT, SMTP, and RDS values must not be pasted in an interview. Would-do: Secrets Manager.
3. Leadership Principles (ConvBI only)
Ownership — Ylogx primary
If they name Ownership, this is the first story. Backup if Ylogx already filled the hour: Horizon GStreamer + costmap — not a fourth ConvBI trophy.
Q1. Tell me about a time you took ownership of a problem in ConvBI primary
Situation
The intern ticket could have ended when the chatbot returned rows. App-only org checks would still let a generated JOIN cross a tier. A green demo was incomplete.
Task
I was not a named security team. I still owned correctness for three organizational tiers on the shared bot and reporting path.
Action — I did
- I put RLS and RBAC in Postgres so generated SQL ran as the caller’s non-owner role.
- I mapped identity to three org-shaped tiers on chatbot and report builder, not a React hide.
- I fail-closed to rls_no_access after a RESET ROLE leak class, and I kept that mapping on the CloudFront / ALB / ECS path.
Result
Three-tier isolation in the database. 99.9% uptime. 40% faster reports on the same identity-to-role path.
Follow-up: Why did you take ownership? It was not your ticket.
The leak class sits on the path I was already shipping. HTTP 200 with the wrong org is a product bug, not a future epic. Waiting for a security owner would have shipped the demo.
Follow-up: What resistance?
I do not invent a manager fight. The discarded options were app-only WHERE, middleware-only, and “the model will add org_id.” I argued those fail on the first missed handler.
Follow-up: What would you do differently?
Denied-tier JOIN fixtures from day one, including a mapping table. Status-aware commit/rollback.
- I for RLS I designed in this Nest tree. Intern title, not manager. 99.9% is serving, not a leak-ticket SLA.
Q2. Tell me about a production issue you owned primary
I do not invent a PagerDuty ID. I speak a class this architecture had to own:
Situation
A generated JOIN can be valid SQL and still return another organization’s facts through a mapping table. HTTP still says 200. The KPI looks right.
Task
Stop treating “SQL ran” as done. Own the isolation class on the live path.
Action — I did
- Execute as caller role with RLS on joined tables.
- Fail-closed when db_role_name is missing.
- Keep executeWithoutRLS off warehouse tables — it is for chat metadata after RESET ROLE.
Result
Isolation in Postgres. 99.9% held. No invented incident count.
Follow-up: How did you debug?
Ask: which role was SET? Was this executeWithoutRLS? Did the JOIN go through a table without a policy? Did the cache key omit tier? Logs: LoggingInterceptor duration/status; SQL_LOGGING for statements. I do not invent a Datadog trace ID.
Q3. Tell me about an initiative that went wrong notes
Notes STAR — client-side aggregation. Same crash as Highest Standards with an Ownership lean-in: I sent the defect into the operator’s tab.
Situation
I aggregated report charts in the browser. A large slice crashed the UI and destabilized the agent path.
Task
Keep heavy aggregation off the client. Do not weaken RLS to “fix” a UI crash.
Action — I did
- Server/preload for AI-requested reports.
- Click to load full data.
- Cleanup after a heavy request.
- Multi-chart via LLM path, not images.
Result
UI rendered; agent stable. 40% / 99.9% stay product families, not this crash’s study.
Dive Deep — Ylogx primary (default if they do not name an LP)
Speak Redis and RLS together. Anecdote “the model is slow” versus measured bot-DB −35%, plus JOIN leak if isolation is only in the app.
Q1. Tell me about a difficult technical problem you investigated primary
Situation
The happy-path demo said the chatbot worked. Two facts disagreed. Every NL turn still hit Postgres, so latency felt like the model. And if isolation lived only in the app, a generated JOIN could leak. The same confident response could be slow, wrong-grained, or leaked.
Task
Measure the query path. Put isolation where the JOIN actually runs.
Action — I did
- I measured bot-database work instead of stopping at “GPT is slow.”
- I put Redis cache-aside with tier and role in the key; miss still ran as caller with RLS.
- I put SET ROLE on a per-request transaction so a legal JOIN still lost the other org’s rows.
Result
Bot DB latency −35%. Three-tier isolation in Postgres. 99.9% held. The deep dive was latency plus isolation on the same path.
Follow-up: What assumption was wrong?
That “the model is thinking” was the whole latency story, and that an application WHERE was enough isolation.
Follow-up: What logs/metrics?
Bot path duration vs SQL duration when SQL_LOGGING is on. After Redis, repeats should not re-hit schema. I do not invent p95 or a hit-ratio dashboard. Resume number is −35% bot DB.
Follow-up: Root cause in one sentence?
Repeat database work on the NL path, plus isolation that was not in Postgres, so a green HTTP status was not a correct tenant.
Q2. Walk me through your debugging process on a wrong-org KPI primary
Situation. Legal SQL, HTTP 200, wrong tenant.
I would check, in order: (1) Was SET ROLE actually run, or rls_no_access? (2) Did this handler use req.dbTransaction or a new connection? (3) executeWithoutRLS? (4) JOIN table missing a policy? (5) Cache key missing tier? (6) SUPER_ADMIN RESET ROLE used by mistake on a tenant user?
Follow-up: What if the database is slow, not down?
Pool waits (acquire 30s). Per-request transactions hold connections. Symptoms: rising latency, not necessarily 5xx. Would-do: statement_timeout, skip SELECT *, indexes on filter columns. I did not ship PgBouncer in this repo.
Frugality — Ylogx primary
Q1. Tell me about a time you accomplished more with less primary
Situation
Every NL turn that hit Postgres was latency and load. The obvious spend was a larger RDS.
Task
Cut hot-path database work without buying instance size, and without weakening isolation.
Action — I did
- Measure bot-DB latency first.
- Redis cache-aside, tier and role in the key.
- Miss and Redis-down still as caller role. CloudFront for static JS only — private JSON is not an edge object.
Result
−35% bot DB latency. 99.9% held on the existing ECS path. No bill on the resume.
Follow-up: Why not a bigger RDS?
Repeats are cache-shaped. A larger database does not fix the same English twice, and it does not fix a JOIN leak.
Follow-up: Is the in-memory Map Frugality?
It avoids extra calls for dataset fields in one process. It is not the resume −35%. If they open QueryDBService, I say Map, 5 minutes, table-name key, and that a tenant-safe Redis is the resume hop.
Customer Obsession — ConvBI is the backup (GiftedBooks 3–10 min is the first story)
Q1. Who is the customer of ConvBI, and what did you change for them? backup
Situation
Operators and non-SQL users needed reports, ad-hoc questions, and live KPIs over shared warehouse facts without reading another org’s book.
Task
Ship an interface they can use daily: English → inspectable SQL, tiles that do not wait on a model, reports that do not crash the tab.
Action — I did
- SQL RAG so they are not blocked on an analyst for a SUM.
- 30 deterministic dashboards at p90 under 210 ms.
- After I crashed a large report in the browser, I moved aggregation off the client.
Result
+65% analysis productivity, +60% ops, 40% faster reports. Protocol sentence on percents.
Follow-up: Did you talk to customers?
I interned on the product they used. I do not invent a named persona interview. The crash reached the operator’s tab — that is the feedback loop I acted on.
Tighten: If they already heard GiftedBooks this hour, do not restart Customer on ConvBI unless they stay on Ylogx. If GiftedBooks is free, speak 3–10 min doubts first.
Deliver Results — ConvBI is the backup (Horizon 17th / 80+ is first)
Q1. Tell me about a time you delivered a hard result at Ylogx backup
Situation
Daily queries, custom reports, and tiles had to ship on the intern calendar, isolated, without waiting on a model for every KPI.
Task
Land the report builder, SQL RAG, and 30 dashboards on ECS with isolation already on.
Action — I did
- Report builder + identity path.
- SQL RAG propose/execute.
- Deterministic tiles, CI/CD to ECS.
Result
40% faster reports, 99.9% uptime, 30 dashboards +60% ops. If they already heard Horizon 17th, this is the backup Deliver story — not a second Horizon speech.
- 17th / 80+ is Horizon we. 40% and 99.9% are Ylogx I/we product numbers. Do not mix ERC into this card.
Invent and Simplify — ConvBI is backup (IQVIA Hybrid RAG is first)
Q1. What did you simplify in ConvBI? backup
Situation
Dumping warehouse rows into a prompt looks simple and is not. The model invents totals and cannot inherit RLS.
Task
Make the model the interface, Postgres the calculator.
Action — I did
- NL → SQL only, allow-listed tables, execute as caller.
- Two services: Python proposes, Nest executes under SET ROLE — simpler trust boundary than one god-object.
- No LLM on dashboard GET.
Result
+65% on the bot path. p90 under 210 ms on tiles because we did not “invent” a model there.
Follow-up: What did you sacrifice?
The bot cannot answer questions outside the ten tables. WITH queries need a wrap. ChatOpenAI can still propose a bad JOIN — Nest and RLS have to catch it. That is the point of simplify: fewer ways to skip isolation.
Learn and Be Curious — ConvBI is backup (IQVIA LangGraph is first)
Q1. What did you have to learn to ship isolation? backup
Situation. Application WHERE looked enough until I thought about generated JOINs.
I learned: Postgres SET ROLE is one active role; GUCs can be transaction-local; RLS policies see current_setting; a connection pool can leak a role if you do not bind work to one transaction; FastAPI must not run the warehouse SQL.
Follow-up: How did you learn it?
Reading pg policies, shipping the middleware, watching the leak class on RESET ROLE, then fail-closing. I do not invent a course certificate.
Tighten: If they name Learn first, IQVIA LangGraph is the spoken story unless they already used IQVIA.
Highest Standards — ConvBI is backup (Argus 89% + 24 FPS before cameras is first)
Q1. Tell me about a quality bar you refused to ship backup
Two ConvBI bars, pick one if Argus is already used:
A. Dashboards: I refused a model call per tile. That would miss p90 under 210 ms and invent KPIs. Resume 99.9% / p90 under 210 ms live here.
B. Notes crash: Client aggregation crashing the UI was not “good enough for an intern demo.” Server path + click-to-load. Do not hang 40% on that crash.
Follow-up: Where did you still leave a gap?
GET table data is SELECT * unbounded. FinishTransaction commits on HTTP finish even if the status is 5xx. Task-def still inlines secrets. I can name those without claiming I already fixed them.
Earn Trust — ConvBI is backup (GiftedBooks grounded RAG + README mismatch is first)
Q1. Tell me about a time you were honest about a security or quality miss backup
If they open GitHub on Conv-BI: cookie markdown is not jwt.middleware.ts; Redis is not in this Nest package.json; 9 dashboard types vs resume 30 tiles. I say the split instead of papering it.
Product honesty: I named the JOIN-leak class out loud — legal SQL, wrong tenant, HTTP 200 — and put isolation in Postgres. 99.9% held.
- 99.5% is GiftedBooks. 99.9% is Ylogx. Do not swap them.
Have Backbone; Disagree and Commit — ConvBI is backup (IQVIA hybrid vs vector-only is first)
Q1. Tell me about a technical disagreement on ConvBI backup
Situation
The easy path was app-only WHERE org_id and “the model will add the filter.” That ships a demo.
Task
I needed isolation that still holds on a JOIN the UI never rendered.
Action — I did
- I argued RLS in Postgres plus execute as caller, not table owner.
- I discarded trusting the LLM with authorization.
- Once we committed to SET ROLE on a transaction, I did not keep a second ad-hoc WHERE style in the warehouse path.
Result
3-tier RLS in the database. I do not invent a manager quote or a fight.
Tighten: If they name Backbone first, IQVIA hybrid vs vector-only is the story unless IQVIA is already spent.
Are Right, A Lot — ConvBI is backup (Argus 73→89 is first)
Q1. Tell me about a time you used data instead of an anecdote backup
Anecdote: the model is slow. Measurement: bot-database latency, then Redis −35% on that family. I still kept RLS on the miss path so a faster leak was not “right.”
Tighten: Argus 73% mAP not shippable is the first Are Right story.
Bias for Action — ConvBI is weak here (Horizon ERC date is first). Do not steal the rover calendar.
Q1. When did you ship a fix without a long design doc? backup
After the RESET ROLE leak class, I shipped fail-closed rls_no_access instead of leaving admins on RESET ROLE. After the client aggregation crash, I shipped server/preload and click-to-load rather than waiting for a rewrite of the whole canvas.
Tighten: If they name Bias for Action, Horizon ERC date is the spoken story.
Think Big — ConvBI is weak here (StratifyLabs marketplace is first)
Q1. What was the larger product bet? backup
BI that non-SQL users can query in English, with isolation already in Postgres, and tiles that stay deterministic. That is a product direction, not Stratify’s 50+ catalog. I do not force Think Big onto ConvBI if Stratify is free.
Tighten: Speak Stratify first. IQVIA 200+ sites only if Invent/Learn/Backbone have not already spent IQVIA.
Success and Scale — ConvBI is backup (Argus 20+ cameras after the quality gate is first)
Q1. How did you scale serving without skipping isolation? backup
ALB + ECS tasks, Docker, CI/CD, RLS already on the request. Scale is more healthy tasks and a bounded pool, not skipping SET ROLE. I did not shard. I did not operate EKS.
Tighten: Argus 20+ cameras after 89% / 24 FPS is the first Success and Scale story.
Hire and Develop / Earth’s Best Employer — no ConvBI card
I do not invent mentees or a ConvBI pairing program. Hire and Develop: IEDC Tech Team member. Earth’s Best: Horizon knowledge sharing. If they force a Ylogx angle, I say I do not have a credible intern-as-manager story and I offer a technical LP instead.
4–14. Technical sections
4. Architecture deep dive
Walk me through the architecture of ConvBI primary
Browser React SPA talks REST to Nest on port 5000. Nest authenticates with Bearer JWT, opens a Sequelize transaction, SET ROLE, then controllers use that transaction against Postgres with RLS. Dashboards and reports stay in Nest. Natural language goes Nest → FastAPI sidecar which returns SQL and chart metadata; Nest executes the SELECT. Tally zips go to S3 then a gateway. Static JS is CloudFront; API is ALB → ECS.
Why this architecture?
Isolation has to run where SQL runs. The model must not be the policy engine. Tiles must not wait on generation.
Why Postgres?
Relational warehouse facts, transactions, and RLS. I discarded Mongo for BI facts. Prisma is a skills line, not this intern ORM — we used Sequelize.
Why Nest and FastAPI?
Nest already owned auth and the request transaction. LangChain’s SQLDatabase stack is Python. Propose vs execute is the split, not “Python is faster.”
Where is auth? Validation?
JWT on almost every route. ValidationPipe whitelist. FastAPI does not authenticate the warehouse caller — Nest is the trust boundary.
What is stateful?
Postgres is the source of truth. ECS tasks are stateless. Redis on the bot path is cache, not source of record. The in-process Map is per task. Chat rows live in Postgres.
What scales horizontally?
Nest and FastAPI tasks behind ALB. Postgres does not shard in what I shipped. Redis would be shared would-do. CloudFront scales static.
Downstream FastAPI fails?
Nest returns 502 on chatbot HTTP error. Tiles still serve. Would-do: timeout budget, limited retries with jitter on that GET-shaped call only.
- Port 5000 in code. Latex 3000 is stale. CodeBuild latex vs Actions YAML — speak the file.
5. API deep dive
How is the API designed?
REST, JSON, Swagger at /api with Bearer. Controllers are thin; services take req.user and req.dbTransaction. Excluded from JWT: health*, signup/signin/otp/activate/forgot/reset/update-password/refresh-token.
Idempotency?
Shipped: dashboard GETs are naturally idempotent. Understand: POST /reports and Tally upload are not safe to retry blindly. Would-do: Idempotency-Key on uploads.
public boolean alreadyProcessed(String idempotencyKey) {
return store.putIfAbsent(idempotencyKey, Boolean.TRUE) != null;
}
Rate limiting?
Not shipped in this Nest tree. Resume Redis is cache-aside, not a rate limiter. Understand: token bucket at ALB/API Gateway or in-process per user.
// Understand — not a ConvBI shipped limiter
class TokenBucket {
private final long capacity, refillPerSecond;
private double tokens; private long lastNanos;
synchronized boolean allow() {
long now = System.nanoTime();
tokens = Math.min(capacity, tokens + (now - lastNanos) / 1e9 * refillPerSecond);
lastNanos = now;
if (tokens < 1) return false;
tokens -= 1; return true;
}
}
Pagination?
Receivables dashboard page/pageSize (default 12, max 100). execute-query LIMIT 1000–5000. getTableData has no limit — I name that if they open it. Understand: keyset pagination beats OFFSET at 10×.
6. Database deep dive
How is data modeled?
App tables are Sequelize models (users, roles, policies, chats, reports, …). Warehouse tables (sales, bs, pl, outstanding_*, cashflow_*, inventory_*) are queried as SQL under RLS — not ORM entities in this tree.
Indexing?
I do not invent a named index list the PDF does not print. Would-do: indexes on filter columns used by EXTRACT and organizationId, and on last_updated_time for freshness APIs. Trade-off: write cost on Tally ingest.
Transactions?
One transaction per authenticated request for RLS. FinishTransaction commits on HTTP finish; rollback only if commit throws. Chat metadata uses a second transaction with RESET ROLE. Understand: I would inspect statusCode before commit.
synchronize: true?
On in sequelize.module.ts. Would-do: migrations, synchronize off in prod. Highest Standards follow-up if they open that file.
Pool leftover?
max 20, acquire 30s, idle 10s. Each request holds a connection until finish. 10× QPS without shorter handlers waits in the pool. Would-do: statement_timeout, PgBouncer, do not open a tx for /health/simple.
7. Performance and optimization
How did you optimize the hot path? primary
Three families. I keep them separate.
Dashboards — p90 under 210 ms: no LLM, CloudFront JS, pooled SQL, JSON only.
Bot DB — Redis −35%: cache-aside, tenant in the key.
Reports — 40% faster: builder + server aggregation after the client crash, LIMIT on execute-query.
How I measured
PDF families. I will not invent Grafana panels. LoggingInterceptor duration is in this tree. SQL_LOGGING + slow >1s.
Bottleneck identification
Split “model time” from “SQL time.” If SQL dominates repeats, cache. If a tile is slow, look at sequential aggregations and SELECT *.
Connection pooling
Sequelize pool max 20. Helps reuse connections. Does not help if every request holds a SET ROLE tx until the client finishes downloading 50mb.
Caching trade-off
Faster repeats vs stale + leak if the key is wrong. Fields Map is not tenant-keyed — metadata only, 5 min, would-do to key by role if columns can be authorization-sensitive.
Would CDN cache API JSON?
No. Private tenant JSON is not a CloudFront object. CloudFront is the SPA.
8. Reliability and 99.9% — deepest grind
What does 99.9% mean?
About 43 minutes of downtime per month, or 8.77 hours per year, if you measure continuously. The resume prints 99.9% on Ylogx serving. It does not print the window. I do not fake one.
SLA vs SLO vs SLI?
Understand: SLI is the measurement (successful health, or request success). SLO is the target (99.9%). SLA is a customer contract. I did not ship a written customer SLA document I can quote.
Liveness vs readiness?
Understand: liveness = process up (/health/simple). Readiness = can take traffic (/health Postgres ping). If ALB uses liveness only, a task with a dead RDS still gets traffic. I would point ALB at Terminus /health.
Timeouts?
axios 10s on most SPA calls. Chat uses fetch without that 10s cap. AI gunicorn timeout 180. Tally gateway axios 30s. Understand: a timeout bounds the caller so a hung SQL does not hold the pool forever. “It will eventually succeed” still occupies a connection.
Retries + exponential backoff + jitter
public static void retry(Runnable op, int maxAttempts) throws Exception {
Random rnd = new Random();
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try { op.run(); return; }
catch (Exception ex) {
if (attempt == maxAttempts || !isTransient(ex)) throw ex;
long backoff = (1L << (attempt - 1)) * 100L;
long jitter = rnd.nextInt(100);
Thread.sleep(backoff + jitter);
}
}
}
Why exponential? Immediate retries pile onto a sick dependency. Why jitter? Independent clients otherwise retry in lockstep (thundering herd). What is safe? Idempotent reads. Not Tally POST, not “create report” without a key.
Circuit breaker — understand, not shipped
After N failures, stop calling the sidecar for a cooldown, fail fast, try a probe. Prevents cascading. I did not ship Resilience4j. Would-do on FastAPI 502s so Nest dashboard traffic is untouched (it already is — different code path).
enum State { CLOSED, OPEN, HALF_OPEN }
// CLOSED: calls pass. Too many failures → OPEN (fail fast).
// After cooldown → HALF_OPEN (one probe). Success → CLOSED.
Graceful degradation
Shipped shape: chatbot error still returns a sentence; tiles do not use the model. Redis down → SQL. Would-do: show stale tile timestamp from last-updated API rather than a spinner forever.
Rolling deploy / rollback
ECS wait-for-service-stability after a new image. Prod has a 3-minute human approval. I do not claim blue/green or canaries unless they ask how I would add them — two target groups, shift traffic, rollback by moving the listener.
Backups / DR
Not a named runbook on the resume. Understand: RDS automated backups, PITR, S3 for Tally zips already off-box. I did not operate a second region.
Single points of failure
Postgres primary. Chatbot URL. Approval person on prod (3-minute window). Secrets in task-def. One ECS service name in YAML. Honest 10× starts here, not with Kafka.
9. Scalability
What breaks first at 10× traffic?
The connection pool and held transactions, then unbounded SELECT *, then sequential dashboard queries, then RDS CPU. Not “the React app.” CloudFront will still serve JS.
Horizontal scaling
Add ECS tasks. Stateless API. Watch max_connections vs 20×tasks. I did not shard.
Load balancing
ALB to healthy tasks. Health must include Postgres for readiness.
Multi-region?
Would-do, not shipped. RLS and cache keys still have to be tenant-correct. Latency vs consistency on warehouse ingest from Tally.
10. Security
How do you authorize a request?
Bearer JWT → role on req.user → SET ROLE / rls_no_access → policy tables → FeatureAccess for routes. bcrypt on passwords. Org-domain signup. Zip magic bytes on Tally.
JWT in localStorage vs cookies?
This checkout: localStorage, signin returns Bearer prefix, axios re-adds Bearer. Cookie MD is a migration write-up. Understand: HttpOnly cookies reduce XSS token theft and need CSRF care. I speak the file they opened.
SQL injection?
ValidationPipe + allow-list tables + SELECT prefix. Org id is interpolated today — would-do bind parameters. SET ROLE interpolates an admin-created role name. Date filters on execute-query use binds.
What must GenAI never do?
Author RLS, run as table owner, execute writes from NL, decide cache authorization, calculate the 30 tiles, approve rollouts.
11. Failure scenarios
FastAPI / chatbot down
Tiles work. Bot: 502 or frontend fallback sentence. Isolation unchanged.
RDS down
SQL paths fail. /health should fail. /health/simple may lie. Would-do: ALB uses /health.
Redis down
Resume hop: Postgres as caller role. Nest Map: one task’s field cache cold. Isolation must still hold.
One ECS task dies
ALB should drain if health checks fail. Remaining tasks take traffic. Session is JWT, not sticky memory (except the Map cache).
Wrong-org JOIN
RLS + caller role. If it still happens: missing policy on a joined table or executeWithoutRLS misuse.
LLM invents a table
hasTableAccess + allow-list. Query rejected or empty with error message.
Tally gateway timeout
30s axios; zip already on S3. Retry upload is not blindly idempotent without a key.
Prod approval missed 3 minutes
Workflow approval step times out. Image may be in ECR without a service roll. Human process, not 99.9% math I will invent.
12. Code-level questions (Java for Live Code)
Intern implementation was TypeScript/Python. If they switch to a whiteboard, I write Java and say the split once.
Retry with exponential backoff
Exception last = null;
for (int i = 0; i < 3; i++) {
try { return callChatbot(sql); }
catch (IOException ex) {
last = ex;
Thread.sleep((1L << i) * 100 + ThreadLocalRandom.current().nextInt(50));
}
}
throw last;
Why not retry immediately?
The dependency is likely still hot. You add load.
Why jitter?
Unsynchronized sleep still aligns thousands of clients. Random spread breaks the herd.
What is safe to retry?
GET dashboard. Not POST tally, not “create report” without Idempotency-Key. SQL RAG execute is read-only SELECT — retry is safe if you bound the time/rows.
Cache-aside (Redis story in Java)
String key = tier + "|" + role + "|" + fingerprint; String hit = redis.get(key); if (hit != null) return decode(hit); // still isolated by key String rows = db.queryAsRole(sql, callerRole); // RLS on miss redis.set(key, encode(rows)); // never cache another tier return rows;
Stale data?
TTL or schema version in the key. PDF has no TTL. I will not invent one.
Cache stampede?
Understand: single-flight per key, or probabilistic early expire. Not shipped in this Nest Map.
LRU (understand)
LinkedHashMap<K,V> lru = new LinkedHashMap<>(16, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry<K,V> e) { return size() > cap; }
};
QueryDB uses TTL expiry, not LRU. LRU is the “how would you bound memory” follow-up.
Connection pool vs thread pool
Pool of 20 DB connections is not a Java thread pool. Holding a connection across an HTTP call to FastAPI (chat-query waits on the model) is the danger: SET ROLE tx sits idle while GPT thinks. Would-do: do not hold the RLS tx during the FastAPI round-trip — open it after SQL is proposed. If they open executeChatQuery, that wait happens before executeRawSelectQuery; the RLS tx was already opened in middleware at request start. That is a real follow-up: chatbot holds a pool slot for the model.
Circuit breaker vs timeout
Timeout: this call has a budget. Circuit: stop calling a sick dependency after a pattern of failures. You want both. Timeout without a circuit still storms. Circuit without timeout waits on the last in-flight call.
13. Questions that can make you go blank
What was the hardest technical decision?
Execute generated SQL as the caller role in Postgres, not as table owner and not as “the model promised org_id.” Everything else (Redis, tiles without LLM) hangs off that.
What did you implement incorrectly at first?
Client-side aggregation (notes). RESET ROLE fallback for admins with no SYSTEM role (leak class → rls_no_access). I do not invent a third fake bug.
Tell me about an optimization that would have been wrong
Bigger RDS first. CloudFront for private answers. Redis keys from raw English. LLM on every tile. All four look like performance and are isolation or cost mistakes.
What breaks first at 10×?
Pool + held transactions, then SELECT *, then RDS. See Scalability.
Biggest technical regret?
Leaving GET table data unbounded, committing the RLS tx on every HTTP finish, inlining secrets in task-def, fields cache keyed only by table name. I can say those without claiming I already shipped the fix.
What would you redesign today?
Propose SQL without holding the RLS connection; parser not prefix; Secrets Manager; ALB on /health; tenant-keyed shared cache; synchronize off.
Biggest SPOF?
Postgres primary, then chatbot host, then prod approver window.
What if Redis is down?
SQL as caller. Latency back. Isolation holds. Tiles do not depend on Redis.
DB slow rather than down?
Acquire waits, tail latency, maybe not 5xx. Timeouts and less held-tx work. Health may still pass if ping is cheap.
Sudden 5× latency — how do you debug?
Is it tiles or bot? Tiles: SQL_LOGGING, which dashboard, EXTRACT full scans, pool wait. Bot: Redis miss storm, FastAPI/OpenAI, held tx during model call. Deploy: did we just roll? I do not invent Datadog, but I would look at ALB target response time and RDS connections.
Memory leak in Node?
fieldsCache Map grows by table name and never evicts except TTL — bounded by table count, not by users. A true leak would be unbounded report JSON in memory (50mb body). Heap snapshot would-do. Client crash was browser memory, not the Node heap.
Traffic spike?
ECS desired count would-do. Cache repeats. Do not retry-storm. Chatbot is the expensive path — degrade it first, keep tiles.
Duplicate requests?
GET tiles: ok. POST tally/report: need a key. Browser double-click on upload.
Multi-region?
Would-do. Warehouse ingest and RLS policies have to be in both. I did not ship it.
What would you monitor / alert?
ALB 5xx, /health fail count, ECS running vs desired, RDS connections vs max, chatbot 502 rate, p90 tile latency vs 210 ms family, not a fake cache hit SLA.
How do you prove an optimization worked?
Same endpoint, before/after duration from interceptor or SQL log, same filters. PDF percents: mechanism + number + no protocol. I will not mint a notebook p95 in the hour.
Manager disagreed with RLS in the database?
I do not invent a fight. I would show a legal JOIN that returns the wrong org under app-only WHERE, then commit to SET ROLE once we agreed. IQVIA hybrid vs vector is the named Backbone story if they want interpersonal conflict.
14. Rapid-fire
| Prompt | One-sentence SAY |
|---|---|
| ORM? | Sequelize. Prisma is skills-line, not this intern. |
| Auth header? | Bearer. Cookie MD is not this middleware. |
| Chatbot model if they open GitHub? | ChatOpenAI gpt-4.1, temperature 0. Resume silent. |
| LangGraph here? | No. IQVIA. |
| Redis in package.json? | No. Resume −35% is still Redis on the bot path. |
| Dashboard LLM? | No. That is how p90 under 210 ms stays possible. |
| executeWithoutRLS? | Chat rows. Not warehouse. |
| First db_role_name? | Postgres one SET ROLE. |
| Prod approve? | Arjun-ylogx, 3 minutes, then ECS wait. |
| Port? | 5000. |
| Pool? | max 20. |
| Join types? | INNER LEFT RIGHT FULL. |
| Tally? | Zip magic-bytes → S3 → gateway. Not bytea. |
| Java intern? | No. Live Code is Java. |
| EKS? | Not this intern hop. ECS. |
| 99.9% formula? | PDF number + serving hop. No invented window. |
| Default unnamed LP? | Dive Deep Redis+RLS, then Deliver Horizon 17th. |
Next project pages are not in this file yet. Finish ConvBI before IQVIA / Horizon / Argus.