Interview Prep Hub

Software Engineer (+ Technical PM Track) — Capital Formation Fintech

Location: Lower Parel, Mumbai · Full-time
Domain: Capital markets fintech for real estate, infrastructure, social infrastructure & energy
Role shape: First in-house engineer — hands-on builder evolving into Technical PM
Core Tech Stack: Next.js 14 (App Router) + TypeScript, NestJS, Supabase (PostgreSQL, Auth, Storage), Prisma, Tailwind CSS, Railway
NON-NEGOTIABLE: Daily, fluent use of Claude Code / Codex for AI-assisted development. Speed of shipping is the #1 value.

What the Company Builds

The company is building the digital backbone for capital formation in four large but unorganized sectors — real estate, infrastructure, social infrastructure, and energy. Today these industries raise capital through personal contacts, spreadsheets, and opaque relationship-driven processes. The product brings structure, transparency, and speed via two platforms on a single intelligent matching engine:

  • The Deal Hub (Equity): An AI-powered marketplace that matches project owners/founders with investment bankers and investors based on sector, stage, deal size, and geography. It digitizes the whole deal lifecycle — valuation, secure data rooms, due diligence, and closure.
  • The Credit Hub (Debt): Mirrors offline lending — instead of connecting a borrower directly to a lender, it matches a borrower to the right debt syndicator (DSA), who then approaches the lender and runs the process end to end.
  • Admin & Connection portals: Round out the platform. All four portals live in one Next.js app.

The Role & Work Structure

This is the company's first in-house engineer. The core platform is built by an external development agency, but they need someone in-house who understands the product deeply, owns the project from their side, and builds key pieces (especially wireframes and MVPs) directly. It is a hybrid engineering + technical project management role — you start as a hands-on builder shoulder-to-shoulder with the Founder and grow into owning the technical roadmap and managing the agency relationship.

ResponsibilityWhat it looks like day-to-day
Build in-houseWireframes, prototypes, and MVP components for fast iteration — using Claude Code to move at speed.
Translate visionWork directly with the Founder to turn product vision into clear, buildable specs.
Manage the agencyReview the external agency's work for quality, timelines, and architecture.
Own deliveryScope, track progress, unblock, and ship the project end to end.
Hold the whole pictureUnderstand both hubs deeply enough to make sound technical decisions across the platform.

Job Description Breakdown & Strategy

Here are the themes the Founder will probe and how to prepare for each:

  1. Claude Code / AI-assisted shipping (the gate):
    • JD Expectation: "If you don't already use Claude Code / Codex daily, this role isn't the right fit."
    • Preparation: Be ready to walk through your real Claude Code workflow — how you scope a feature, drive the agent with context (CLAUDE.md, plan mode, subagents), review its output critically, and keep velocity high without shipping slop. Bring concrete examples of things you shipped fast with it.
  2. TypeScript + Next.js + NestJS full-stack depth:
    • JD Expectation: Strong TypeScript; React/Next.js front end; Node.js backend (NestJS ideal).
    • Preparation: App Router server vs client components, NestJS module/provider/DI model, REST API design, and how the Next.js front end talks to the NestJS API.
  3. PostgreSQL + Prisma + Supabase:
    • JD Expectation: Comfort with PostgreSQL and an ORM (Prisma ideal); bonus for Supabase (Auth, Storage, Postgres).
    • Preparation: Prisma schema/migrations, relations and avoiding N+1, Supabase Auth JWTs, Row Level Security, Storage buckets + signed URLs, and pooled vs direct connections.
  4. Wireframe → MVP → product:
    • JD Expectation: Take a feature from wireframe to MVP to working product.
    • Preparation: Show a repeatable process: clarify the user problem, sketch the flow, build a thin vertical slice, validate with the Founder, then harden. Tailwind for rapid UI.
  5. Technical project management:
    • JD Expectation: Manage and review the agency, own scope/timeline/architecture, drive delivery.
    • Preparation: How you review external code, set architecture guardrails, track delivery, and communicate trade-offs to a non-technical Founder.
  6. Domain & ownership in ambiguity:
    • JD Expectation: Self-starter who thrives on speed, ownership, and ambiguity; bonus for fintech / capital markets / lending / real estate exposure.
    • Preparation: Map your end-to-end ownership stories. Speak to financial data correctness, audit trails, and secure document handling — directly relevant to deal rooms and due diligence.

Preparation Guide — Recommended 10-Day Sprint

DaysFocusOutcome
1Claude Code workflow story + the company's product (Deal Hub / Credit Hub / matching engine)Crisp AI-dev narrative; can explain both hubs in plain English
2–3TypeScript depth + Next.js 14 App Router (server/client components, data fetching, route handlers)Front-end fluency on their exact stack
4–5NestJS (modules, providers, DI, guards, pipes) + REST API designBackend architecture confidence
6–7Prisma + PostgreSQL (schema, migrations, relations, indexing) + Supabase (Auth, RLS, Storage)Data layer mastery
8Financial data correctness, secure data rooms, RBAC across 4 portals, Railway deploy + envDomain-aware system design
9Technical PM scenarios — reviewing agency work, roadmap, unblockingPM-track readiness
10Behavioral / ownership / ambiguity stories (STAR) + mock with all questions belowInterview-ready

Relevant Curriculum (map to your existing reading materials)

  • TypeScript & Next.js / React: see Next.js & React — focus on App Router, server components, and route handlers.
  • NestJS & Node.js: see Node.js & NestJS — modules, dependency injection, guards.
  • PostgreSQL & SQL: see PostgreSQL & SQL — indexing, transactions, query optimization (Prisma sits on top of this).
  • System Design: see System Design — for the matching engine, data rooms, and multi-portal architecture.
  • Fintech Domain: see Fintech Domain — money handling, audit trails, compliance.
  • Microservices & APIs: see Microservices & APIs — REST design, idempotency, integration reliability.
  • Behavioral & STAR: see Behavioral & STAR — ownership and ambiguity stories.

Top Interview Questions & Answers (27)

AI-Assisted Development (Claude Code)

1. Walk me through how you actually use Claude Code day-to-day to ship fast.

Answer:

I treat Claude Code as a fast pair-programmer that I steer, not autopilot. My loop is:

  • Set context once: I keep a CLAUDE.md at the repo root documenting the stack, conventions, commands, and gotchas. That stops the agent from guessing and keeps output on-pattern.
  • Plan before code: For anything non-trivial I have it produce a plan first (files to touch, approach, trade-offs), correct the plan, then let it implement. Cheaper to fix a plan than a diff.
  • Thin vertical slices: I scope work to one buildable slice — e.g. "create-deal form → NestJS endpoint → Prisma write → list view" — so I can verify behavior, not just compilation.
  • Review critically: I read every diff. The risk with AI dev isn't that it can't write code, it's that it writes plausible-but-wrong code. I check edge cases, auth, and data correctness myself.
  • Parallelize: I delegate independent research/exploration to subagents so my main context stays focused on the change.

The result is I take features from wireframe to working MVP in hours instead of days — which is exactly the speed this role is asking for.

2. How do you keep AI-generated code from becoming unmaintainable slop?

Answer:

  • Conventions as guardrails: Linting, formatting, and a documented file/module layout in CLAUDE.md mean generated code matches the existing patterns instead of inventing new ones.
  • Scope discipline: I instruct it to make the minimal change for the task — no speculative abstractions, no drive-by refactors. Three similar lines beat a premature abstraction.
  • Tests & manual verification: I verify the feature actually works in the browser/API, not just that it compiles. For backend logic I have it write tests alongside.
  • I own the architecture: The agent implements; the design decisions (boundaries, data model, auth) are mine. That keeps the codebase coherent even at high velocity.

TypeScript, Next.js & NestJS

3. In Next.js 14 App Router, when do you use a Server Component vs a Client Component?

Answer:

  • Server Components (default): For anything that fetches data, touches secrets, or renders static/derived content. They run on the server, never ship their code to the browser, and can await data directly. For the Deal Hub I'd render the deal list and detail pages as server components — fast first paint, no API keys exposed.
  • Client Components ("use client"): Only where I need interactivity, state, effects, or browser APIs — forms, filters, modals, the document-room viewer. I push the "use client" boundary as low as possible so most of the tree stays server-rendered.
// app/deals/page.tsx  — Server Component
export default async function DealsPage() {
  const deals = await getDeals();           // runs on server
  return <DealList deals={deals} />;          // pass data down
}

// components/DealFilters.tsx — Client Component
"use client";
import { useState } from "react";
export function DealFilters({ onChange }) {
  const [sector, setSector] = useState("all");
  // interactive filtering lives here
}
4. How do you connect the Next.js front end to the NestJS API cleanly?

Answer:

  • Typed contract: I share types between front end and back end (a shared package or generated types) so a NestJS DTO change surfaces as a TypeScript error in the UI, not a runtime bug.
  • Fetch on the server when possible: Server Components call the NestJS API with the user's token attached server-side, so I never leak credentials to the browser.
  • Single API client: One wrapper that attaches the Supabase JWT, sets base URL from env, and maps error shapes to friendly messages — instead of scattering fetch calls everywhere.
  • Route Handlers as a thin BFF: For browser-only mutations I expose Next.js route handlers that forward to NestJS, keeping the token handling in one place.
5. Explain NestJS's module / provider / dependency-injection model.

Answer:

  • Modules group related features (e.g. DealsModule, CreditModule, AuthModule). Each declares its controllers and providers and what it exports.
  • Providers are injectable classes — services, repositories, the Prisma client. NestJS resolves them through its IoC container.
  • Dependency Injection: I depend on abstractions and let Nest wire them, which makes services testable (I can inject a mock Prisma in unit tests).
@Module({
  controllers: [DealsController],
  providers: [DealsService, PrismaService],
  exports: [DealsService],
})
export class DealsModule {}

@Injectable()
export class DealsService {
  constructor(private readonly prisma: PrismaService) {}

  findForInvestor(investorId: string) {
    return this.prisma.deal.findMany({ where: { matchedInvestorId: investorId } });
  }
}
6. How would you design the REST API for creating and matching a deal?

Answer:

  • Resource-oriented routes: POST /deals, GET /deals/:id, GET /deals?sector=&stage=&minSize=, POST /deals/:id/matches.
  • Validation at the edge: NestJS DTOs with class-validator so bad input is rejected with a 400 before it reaches business logic.
  • Consistent envelope & status codes: 201 on create, 200 on read, 400/401/403/404/409 mapped by error category — never 200 for an error.
  • Idempotency on money-adjacent writes: Accept an idempotency key so a retried "commit deal" doesn't double-record.
export class CreateDealDto {
  @IsString() @MaxLength(200) title: string;
  @IsEnum(Sector) sector: Sector;
  @IsEnum(Stage) stage: Stage;
  @IsInt() @Min(0) dealSize: number;     // store in smallest unit
  @IsString() geography: string;
}
7. Give a TypeScript example of modeling the two hubs with a discriminated union.

Answer:

A discriminated union lets the compiler force me to handle each hub correctly — no "forgot the debt case" bugs.

type EquityDeal = {
  kind: "equity";
  investorId: string;
  valuation: number;
};

type DebtDeal = {
  kind: "debt";
  syndicatorId: string;     // DSA, not a direct lender
  facilityType: "working_capital" | "project_finance" | "growth_debt";
};

type Deal = EquityDeal | DebtDeal;

function route(deal: Deal) {
  switch (deal.kind) {
    case "equity": return matchToInvestor(deal.investorId);
    case "debt":   return matchToSyndicator(deal.syndicatorId);
    // TS errors if a new kind is added and not handled
  }
}

PostgreSQL, Prisma & Supabase

8. Sketch a Prisma schema for the Deal Hub core.

Answer:

I model the actors and the deal, with explicit enums for sector/stage and indexes on the columns the matching engine filters by.

model Deal {
  id            String   @id @default(uuid())
  title         String
  sector        Sector
  stage         Stage
  dealSize      BigInt   // smallest currency unit — never float
  geography     String
  ownerId       String
  owner         User     @relation(fields: [ownerId], references: [id])
  status        DealStatus @default(DRAFT)
  createdAt     DateTime @default(now())

  @@index([sector, stage, geography])   // matching-engine filters
  @@index([ownerId])
}

enum Sector { REAL_ESTATE INFRA SOCIAL_INFRA ENERGY }
enum Stage  { SEED GROWTH LATE }
enum DealStatus { DRAFT OPEN IN_DILIGENCE CLOSED }

Note BigInt for money — I never store currency as a float.

9. How do you avoid N+1 queries with Prisma?

Answer:

  • Use include / select to fetch relations in one round-trip instead of looping and querying per row.
  • Batch lookups with findMany({ where: { id: { in: ids } } }) rather than a query inside a .map().
  • Verify with Prisma query logging — if I see one query per item, that's the smell.
// Bad: N+1 — one query per deal
const deals = await prisma.deal.findMany();
for (const d of deals) {
  d.owner = await prisma.user.findUnique({ where: { id: d.ownerId } });
}

// Good: single query with the relation joined
const deals = await prisma.deal.findMany({
  include: { owner: true },
});
10. What is Supabase Row Level Security and why does it matter here?

Answer:

RLS enforces access rules in the database itself, so even a bug in the API can't leak another tenant's data — critical when deal rooms contain confidential financials.

  • Policies are SQL predicates checked on every row for every query, scoped to the authenticated user (auth.uid()).
  • I'd let only a deal's owner and explicitly invited investors read its documents.
-- Only the owner or an invited participant can read a deal's documents
create policy "deal_docs_read"
on deal_documents for select
using (
  auth.uid() = owner_id
  or auth.uid() in (
    select investor_id from deal_invites where deal_id = deal_documents.deal_id
  )
);

The caveat: when I use Prisma with a service-role connection, RLS is bypassed — so authorization must then be enforced in NestJS guards. I'm explicit about which path each query takes.

11. How do you handle secure document uploads for data rooms with Supabase Storage?

Answer:

  • Private buckets, never public: Due-diligence documents go in a private bucket. Nothing is world-readable.
  • Signed URLs with short TTLs: The backend generates a time-limited signed URL only after checking the requester is authorized for that deal. The link expires in minutes.
  • Authorize before signing: The NestJS guard confirms the user is the owner or an invited participant before issuing the URL.
  • Audit every access: I log who requested which document and when — diligence requires a paper trail.
// NestJS: only sign after an auth check
async getDocumentUrl(userId: string, docId: string) {
  const doc = await this.deals.assertCanAccess(userId, docId); // throws 403 otherwise
  const { data } = await this.supabase
    .storage.from("deal-docs")
    .createSignedUrl(doc.path, 60 * 5); // 5-minute link
  await this.audit.record(userId, "doc.view", docId);
  return data.signedUrl;
}
12. Explain pooled vs direct Supabase Postgres connections and why it matters with Prisma.

Answer:

  • Pooled (PgBouncer, transaction mode): Use this for the serverless/runtime path — short-lived requests share a small pool, which prevents exhausting Postgres connections under load.
  • Direct connection: Use this for prisma migrate and any operation needing prepared statements / a session, which the transaction pooler doesn't support.
  • In practice: I set DATABASE_URL to the pooled connection (with pgbouncer=true) and DIRECT_URL to the direct connection in schema.prisma so migrations and runtime each use the right one.
datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL")     // pooled — runtime
  directUrl = env("DIRECT_URL")       // direct — migrations
}
13. How do you design indexes for the matching engine's filtered search?

Answer:

  • The engine filters deals by sector, stage, deal size, and geography, so I add a composite index covering the common filter combination (respecting the left-most prefix rule).
  • I run EXPLAIN ANALYZE on the real query to confirm it uses the index instead of a sequential scan.
  • For range filters like deal size I order the composite index so equality columns come first, then the range column last.
  • As data grows I'd consider partial indexes (e.g. only status = OPEN deals, since closed ones are rarely searched).
14. How do you run Prisma migrations safely against a live database?

Answer:

  • Migrations in version control: prisma migrate dev locally generates a reviewable SQL migration; prisma migrate deploy applies it in CI/prod.
  • Expand/contract for breaking changes: Add the new column nullable, backfill, switch reads/writes, then drop the old column in a later migration — never a single destructive step on a live table.
  • Backfills are idempotent and batched so reruns are safe and a long migration doesn't lock the table.
  • Use the direct connection for migrations (not the transaction pooler).

Auth, Security & Financial Correctness

15. How does Supabase Auth work and how do you protect a NestJS route with it?

Answer:

  • Supabase Auth issues a signed JWT on email/password login. The front end sends it as Authorization: Bearer <token>.
  • In NestJS I write a guard that verifies the JWT signature against Supabase's JWT secret/JWKS, extracts the user id and role from claims, and attaches the user to the request.
  • Verification is stateless — no DB round-trip per request — and I layer an RBAC check on top for portal-specific permissions.
@Injectable()
export class SupabaseAuthGuard implements CanActivate {
  canActivate(ctx: ExecutionContext): boolean {
    const req = ctx.switchToHttp().getRequest();
    const token = req.headers.authorization?.replace("Bearer ", "");
    if (!token) throw new UnauthorizedException();
    const payload = jwt.verify(token, process.env.SUPABASE_JWT_SECRET);
    req.user = { id: payload.sub, role: payload.role };
    return true;
  }
}
16. There are four portals (Deal Hub, Credit Hub, Admin, Connection). How do you do role-based access cleanly?

Answer:

  • One identity, scoped roles: A user has a role plus the portals/permissions they're entitled to. I encode role in the JWT and keep fine-grained permissions in the DB.
  • Guard + decorator pattern in NestJS: A @Roles('admin') decorator with a RolesGuard declares the requirement at the route, applied at the controller level so it's auditable rather than copy-pasted per handler.
  • Defense in depth: RBAC in the API plus RLS in Postgres, so a misconfigured route still can't leak cross-portal data.
  • Never trust the front end for authorization — hiding a button is UX, not security.
17. How do you handle money correctly in this platform?

Answer:

  • Never use floats. Floating point can't represent decimal currency exactly. I store amounts as integers in the smallest unit (paise) using BigInt, or Postgres NUMERIC for fractional rates.
  • Compute in integer space and format to currency only at the display edge.
  • Wrap multi-step writes in a transaction so a partial failure (e.g. record deal closure + write ledger entry) never leaves inconsistent state.
  • Append-only audit ledger for anything money-related — corrections are new entries, not edits.
18. Why is idempotency important here, and how do you implement it?

Answer:

Network retries and double-clicks happen. For a financial action like committing a deal or recording a payment, processing the same request twice is dangerous.

  • The client sends an Idempotency-Key header (a UUID) with the request.
  • The server stores the key with the result on first processing; a repeat with the same key returns the stored result instead of re-executing.
  • I make consumers of any queue/event idempotent the same way, keyed on a business id.
19. How do you keep secrets and environment config safe across Railway, Supabase, and Vercel/Next.js?

Answer:

  • No secrets in code or git — only env vars, with .env gitignored.
  • Right scope: The Supabase service-role key lives only on the NestJS server (Railway) — never in the Next.js client bundle. Only the anon key and public URL are exposed client-side, and only via NEXT_PUBLIC_-prefixed vars.
  • Per-environment config: Separate Supabase projects / env values for dev, staging, prod so a test never touches production data.
  • Rotate on leak: If a key ever lands in a diff, rotate it immediately.

Delivery, Process & Technical PM

20. Take a feature from wireframe to MVP to production — what's your process?

Answer:

  • Clarify the problem: Confirm with the Founder what user pain we're solving and what "done" means.
  • Wireframe the flow: A low-fidelity sketch of the screens and the happy path — cheap to change, fast to align on.
  • Thin vertical slice (MVP): Build one real end-to-end path (UI → API → DB) with Tailwind for speed and Claude Code to move fast. Validate it live with the Founder.
  • Harden: Add validation, auth, edge cases, error states, and audit logging. Then ship behind a flag if risky.
  • Iterate on feedback rather than gold-plating up front — speed and learning beat perfection.
21. How do you review and manage the external agency's work?

Answer:

  • Set guardrails up front: An agreed architecture, conventions, and a definition-of-done so reviews aren't subjective.
  • Review for the things that bite later: data-model decisions, auth/authorization, money handling, migration safety, and security — not bikeshedding on style the linter already enforces.
  • Track delivery transparently: Clear scope, milestones, and a visible board so timelines slip loudly, not silently.
  • Unblock fast: My job is to remove ambiguity for them — sharp specs, quick answers, and decisions made and owned.
  • Verify, don't assume: I pull their branch and run it, rather than trusting a status update.
22. How do you communicate a technical trade-off to a non-technical Founder?

Answer:

  • Frame it in terms of outcomes: speed, cost, risk, and what the user feels — not jargon.
  • Offer 2–3 concrete options with the trade-off of each ("Option A ships this week but we'll revisit search performance at 10k deals; Option B takes two more days but scales").
  • Give a clear recommendation and the reasoning, so the Founder can decide quickly.
  • Make the decision reversible where possible so we can move fast without betting the company on it.
23. How do you decide what to build in-house vs leave to the agency?

Answer:

  • In-house: Fast-iterating, product-defining pieces — wireframes, MVPs, the matching-engine logic, anything the Founder and I need to learn from quickly.
  • Agency: Larger, well-specified build-out where the requirements are stable and parallel capacity helps.
  • The deciding factor is iteration speed and ownership: if rapid back-and-forth with the Founder is the bottleneck, I keep it in-house so there's no agency round-trip.

Testing & Deployment

24. What's your testing strategy for a NestJS + Prisma backend?

Answer:

  • Unit tests for business logic with the Prisma client mocked, so I test the matching rules in isolation.
  • Integration tests against a real Postgres (a disposable test database or a container) so I exercise actual Prisma queries and migrations — mocking the DB hides query bugs.
  • E2E tests for critical flows (create deal → match → invite → view doc) hitting the booted Nest app.
  • Regression test on every bug: a test that fails before the fix and passes after.
25. How do you deploy and operate the API on Railway?

Answer:

  • Build from git: Railway builds the NestJS service on push; env vars are managed in the Railway dashboard, separated per environment.
  • Health checks & migrations: Expose a health endpoint and run prisma migrate deploy as a release step so schema and code ship together.
  • Stateless service: No in-memory session state, so it scales horizontally; session/auth state lives in Supabase.
  • Observability: Structured logs and error tracking so I can debug production quickly with a small team.
26. How would you add caching without risking stale financial data?

Answer:

  • Cache the safe stuff: Reference data and read-heavy, slow-changing lists (sector taxonomies, public deal summaries) — not live balances or deal status.
  • Short TTLs + explicit invalidation on write, so a deal update busts its cache entry immediately.
  • Never cache authorization decisions across users — a cache key always includes the requesting user's scope.
  • Measure first: I'd only add caching where profiling shows a real hotspot, to avoid correctness risk for no benefit.

Behavioral

27. Tell me about a time you owned something end-to-end under ambiguity.

Answer (STAR):

  • Situation: At 1Finance I owned full modules (Magazine, In-the-News V3, HR Conclave admin) where requirements were loose and I was the sole engineer on them.
  • Task: Take each from a rough product ask to a shipped, production feature without a detailed spec handed to me.
  • Action: I clarified the actual user need, designed the schema and API, built the Fiber endpoints with DB-backed JWT + per-endpoint RBAC, wrote idempotent encryption backfills for legacy rows, and shipped iteratively — validating with stakeholders rather than guessing.
  • Result: Each module shipped end-to-end and is in production. That's exactly the "own it, move fast, fill the ambiguity yourself" posture this first-engineer role needs.

Behavioral & Values Preparation

This is a speed-and-ownership culture working shoulder-to-shoulder with the Founder. Map your stories to these:

  • Speed above all: Show how AI-assisted development and thin vertical slices let you ship working MVPs in hours, then iterate — not months of upfront design.
  • Ownership from day one: You're the first in-house engineer. Tell stories where you took something with no spec and drove it to production yourself.
  • Comfort with ambiguity: Demonstrate making reversible decisions quickly and adjusting on feedback rather than waiting for perfect clarity.
  • Founder-grade communication: Translate technical trade-offs into outcomes and clear recommendations a non-technical Founder can act on.
  • Domain credibility: Emphasize financial-data correctness, secure document handling, and audit trails — the things that matter in deal rooms and lending.