Skip to content

Private

Interview prep

Likely questions for a senior full-stack role, answered in your own voice and grounded in your real work. This page is not linked anywhere in the site navigation.

Intro & positioning

Walk me through your background.

I've been building for the web for about ten years. I started deep on the frontend, a lot of Vue and React with TypeScript, everything from agency and bilingual sites early on to leading the frontend on insurtech products at Luxsurance, where I ran our Vue 2 to Vue 3 migration. Over the last couple of years at VetEngage I've gone properly full-stack. I still lead on the frontend, I set up our monorepo, our CI/CD, and our design system, but I also build features all the way into the backend now: GraphQL resolvers, database migrations, and Temporal workflows in Python and FastAPI. The short version is frontend at heart, full-stack in practice. What I enjoy most is taking a feature from the database to the last pixel.

What are you looking for in your next role?

More of what I already do, but with room to grow. Building meaningful features end to end, working close to the product, on a team that genuinely cares about craft and shipping. I get the most out of building the foundations that make everyone around me faster, so somewhere that values that.

Why are you open to moving?

I keep it simple and positive: I'm looking for a bigger canvas and more scope, not running from anything. I steer the answer toward what I want to do more of.

Behavioral & leadership

Tell me about a large technical change you led.

The one I'm proudest of is leading a Vue 2 to Vue 3 migration at Luxsurance. It was an enterprise insurtech platform, so stopping to rewrite wasn't an option, the business needed features the whole time. The framework changes were honestly the easy part. The hard part was sequencing it into slices small enough that we never left the app broken for more than a short window. My rule was basically that we could pause at any point and still be fine. We kept shipping features through the entire migration, and it taught me that big changes succeed because of how you sequence them, not because the plan is clever.

Describe a feature you built end to end.

At VetEngage I built our patient check-in and walk-in system across every layer. On the backend that's the GraphQL resolvers and schema, the business-logic actions like submit, verify, status, and tokens, the models, the Postgres migrations, and the Temporal workflows for confirmations and notifications, plus the emails and the tests. On the frontend it's a focused, kiosk-style React flow that syncs status straight to the staff dashboard. So a client checks in on a tablet and that flows through the database, a workflow, the API, and back onto the dashboard, and I built all of it.

Tell me about a time you improved how the team works.

When I joined VetEngage I set up the frontend monorepo from scratch, Turborepo and pnpm, and built out our whole CI/CD pipeline: build, test, and lint gates, end-to-end runs, visual testing, automated deploys. And I created our design system, the shared component library everything is built on. None of that ships a feature by itself, but it's the reason a small team can run several apps and have them feel like one product. That's the work I find most valuable, the stuff that quietly makes everyone faster.

A decision you made and later revisited.

A good example is choosing Vite over Next.js for our apps. Most of what we build is an authenticated dashboard and an embeddable booking widget, client-side apps where server rendering and SEO don't really buy us anything, and our data already lives behind GraphQL. Next's server features would have been weight we paid for and never used, and the widget especially wants a small static bundle, not a server runtime. I wrote down the reasoning, including what would change my mind: if we build something content-first and SEO-heavy, Next or Astro is the right tool for that piece. I like making decisions explicit so we can revisit them instead of treating them as permanent.

Tell me about a disagreement on technical direction.

I'll walk through a real one. The way I handle it is to argue from the users and the tradeoffs rather than ego, get to a decision, and then disagree-and-commit if it doesn't go my way. And I write the reasoning down, so if it turns out wrong we can revisit it without relitigating the whole thing.

Tell me about a production incident or a failure.

I'll pick a concrete one: what broke, how I found it, how I fixed it, and most importantly the guardrail I added so it can't happen again, whether that's a test, an alert, or a safer migration. I try to make the story about the system getting more resilient, not just the fire being put out.

How do you mentor or raise the level of a team?

For me it's less about big lectures and more about the environment: good foundations that make the right thing the easy thing, a design system and clear conventions, and defending consistency in code review. If the common path is easy and predictable, the whole team levels up without me needing to be in the room.

Frontend

How do you manage server state vs client state?

I keep them separate on purpose. Anything from the API goes through Apollo Client or TanStack Query, so caching and revalidation are handled for me. Local UI state that doesn't belong on the server goes in Zustand. The mistake I've seen over and over is cramming both into one big global store, and it always turns into a mess. Keeping the concerns apart keeps everything simpler.

How do you architect a design system?

The biggest decision is keeping the components as code in our repo that we control, rather than importing a black-box library. We build on shadcn/ui and Radix, so the components live in our repo as code we control. On top of that: design tokens so theming is just swapping values, Storybook so every component is built and documented in isolation, and Changesets for versioning since other apps depend on it. The payoff isn't prettier buttons, it's that a new feature reuses decisions we already made, so consistency is the default instead of an argument.

How do you find and fix slow React renders?

I start by measuring with the profiler to find the renders that are actually wasteful, rather than guessing. Then it's the usual toolkit where it earns its place: memoization and stable references, virtualizing long lists, and code-splitting so we're not shipping everything up front. The key is measure, fix, measure again, not sprinkling useMemo everywhere on faith.

How do you handle forms and validation?

React Hook Form plus Zod. The nice part is one Zod schema drives both the runtime validation and the inferred TypeScript type, so I've got a single source of truth that's checked at both ends. It kills a whole category of the-data-wasn't-the-shape-I-expected bugs.

What was hard about the Vue 2 to Vue 3 migration?

Mainly the move to the Composition API and the reactivity changes, plus third-party libraries that hadn't caught up yet. But the real challenge wasn't technical, it was doing it incrementally without freezing feature delivery, which is what I optimized the whole plan around.

How do you approach accessibility?

I build it in rather than bolt it on: semantic HTML and proper landmarks, keyboard and visible focus states, Radix primitives so the ARIA is correct by default, and checking contrast and heading order. It's cheaper and better when it's part of building the component, not a pass at the end.

Backend & full-stack

How do you design a GraphQL schema and avoid N+1 queries?

I design the schema around what the client actually needs rather than mirroring the database. For N+1, the answer is batching with dataloaders and being deliberate about what each resolver fetches. And because the schema is typed, it doubles as the contract the frontend codes against, so the two stay in sync.

How do you handle long-running or background work?

We use Temporal for durable workflows. For my check-in features that's confirmations, walk-in notifications, and check-in nudges. I like it over fire-and-forget jobs for durability and visibility: if a step fails it retries, and I can see exactly where a workflow is instead of a task silently disappearing. Anything that has to reliably happen later runs as a workflow.

How do you evolve a database schema safely?

Alembic migrations in version control, and I favor backward-compatible, expand-then-contract changes so a deploy never depends on the migration landing at the exact same instant. I don't run a destructive migration without a plan. I've authored the migrations for my check-in features, so this is day-to-day for me.

How do you keep frontend and backend in sync?

The typed GraphQL schema is the single source of truth. When the frontend codes against it, changing a resolver makes the frontend fail to type-check before anything even runs, so drift gets caught at the cheapest possible moment instead of in production.

Walk me through your check-in system architecture.

It's GraphQL resolvers and schema at the edge, an action layer underneath for the business logic like submit, verify, and status, then domain entities and models, the Alembic migrations for their schema, Temporal workflows for confirmations and notifications, templated email, and tests across all of it. I can trace a single check-in from the tablet tap through each of those layers.

System design

Design an appointment booking system.

I'd start with the availability model and slot generation, then the hard part, which is concurrency: preventing double-booking with a transactional hold or lock on the slot, and making submit idempotent. Timezones matter a lot for appointments, and confirmations and reminders go through durable workflows so they reliably fire. It's close to what I actually build, so I'd anchor the answer in that.

Design an embeddable booking widget.

The constraints drive it: a small self-contained bundle, style isolation so it doesn't fight the host page, clean cross-origin embedding, and config through data attributes. A static build behind a CDN, no server runtime. I've shipped exactly this for our booking widget, so I'd talk from that experience.

How would you scale a monorepo build and CI?

Turborepo's task graph plus caching so only the packages that changed actually rebuild, remote caching so CI and everyone's machines share artifacts, and running only the affected projects on a given change. That's what keeps the pipeline from getting slower every time you add an app, a problem I've dealt with directly.

Design notifications that must not double-send.

Durable workflows with idempotency keys so a retry can't send twice, at-least-once delivery with dedup on the consumer side, and backoff on retries. In dev everything points at a local catcher like Mailpit so we can see exactly what would go out without sending anything real.

Testing & quality

How do you structure a test suite?

The pyramid. Most of it is fast Vitest unit and component tests, with a smaller layer of Playwright end-to-end on the flows that truly can't break, like booking and check-in. Because Vitest runs on the same Vite pipeline as the app, the tests compile the way the app does, so there are no surprises from a separate toolchain.

How do you prevent flaky end-to-end tests?

Almost every flaky test I've chased came down to depending on something the test didn't control. So I make them hermetic: mock the network with canned GraphQL, seed the data the test needs, isolate each test in a fresh browser context, and wait on state like an element being visible instead of a fixed sleep. A flaky test is worse than no test because it trains people to ignore red.

You have no QA team. How do you keep quality up?

It doesn't mean no QA, it means quality is built into how everyone works. A fast base of Vitest tests, Playwright on the critical flows kept hermetic, and before anything merges an exploratory pass with an AI agent driving a real browser through Chrome DevTools, checking the console, network, and what rendered. What that doesn't catch is whether a feature is actually good, so we dogfood and watch our analytics. Tests and AI handle does-it-work, humans still handle is-it-good.

AI-assisted workflow

How do you actually use AI in your workflow?

Heavily but deliberately. I think of it as context engineering: an agent is only as good as what it can see, so I give it the whole stack, both repos running, the monorepo so it sees how components are really used, and the typed GraphQL schema as the contract. I connect it to our tools through MCP, Linear for tickets, Figma for designs, PostHog for product data, so it works from the real ticket and design, not my paraphrase. And I run a spec-to-plan-to-build loop with tests.

How do you keep AI-generated code trustworthy?

Tests come first, because an agent can't fake a passing test, and everything still goes through review across correctness, security, and performance. I treat each step of the workflow as a checkpoint. The agent moves fast between checkpoints, and the checkpoints keep speed from turning into debt.

Why did you move from Cursor to Claude Code?

It was a workflow shift, not a dig at Cursor. I moved from wanting help while I type to wanting to hand an agent a whole task, and Claude Code fit that better, plus the skills, subagents, and MCP ecosystem around it. If someone's day is mostly heads-down typing, Cursor is still great, it just wasn't where my work went.

Questions to ask them

How much end-to-end responsibility do engineers have?

I ask this to find out how much real scope I'd have, and I listen for whether engineers see features through end to end or just close tickets.

How are technical decisions made and recorded?

This tells me whether the team writes down tradeoffs and moves on, or relitigates the same decisions forever.

What is the state of tech debt and testing?

I want to know how much of my time would go to firefighting versus building, and how much they trust their own deploys.

How does the team use AI tooling day to day?

Since AI is a big part of how I work, I want to know whether that's an asset here or a culture clash.

What would success look like in the first 90 days?

It gets me a concrete picture of expectations and ramp, and it's a good note to end on because it shows I'm already thinking about delivering.