cswork / libraryOpen practice workspace

Interview guides · Descartes Underwriting

Descartes Underwriting Software Engineer Interview Guide

Descartes Underwriting Software Engineer interview guide: reported rounds, questions and how candidates describe the process.

The 60-second brief

As a Software Engineer at Descartes Underwriting, you sit at the intersection of high-stakes corporate insurance and cutting-edge scientific modeling. Your mission is to build the technical infrastructure that allows the company to provide parametric insurance—a revolutionary model that triggers rapid payouts based on real-world data, such as weather events or natural disasters. You are not just writing code; you are building the resilience of global businesses against climate risk. The role is inherently multidisciplinary. You will work alongside climatologists, data scientists, and insurance veterans, translating complex physical models into performant, reliable software.

This guide is scoped to a Software Engineer candidate at Descartes Underwriting.

Focus areas

  • Python
  • Time Profiling
  • Python Performance Optimization

The Descartes Underwriting interview process

Descartes Underwriting candidates report 3 rounds over 3-5 weeks. The stages below are what candidates describe, not a published process.

Initial Discovery Calls

Conversations to assess high-level motivation and fit for the role.

What to demonstrate

  • Conversations to assess high-level motivation and fit for the role
  • Depth in Python

How to prepare

  • Answer aloud and timed: How would you profile and optimize a piece of slow-running Python code that performs heavy mathematical calculations?
  • Answer aloud and timed: Explain the trade-offs between functional and object-oriented programming patterns in the context of a scientific simulation.

Take-Home Project

Complete a practical project that serves as the primary artifact for technical evaluation.

What to demonstrate

  • Complete a practical project that serves as the primary artifact for technical evaluation
  • Depth in Python

How to prepare

  • Answer aloud and timed: How do you handle large-scale data ingestion and ensure the integrity of the data being fed into a risk model?
  • Answer aloud and timed: Describe a situation where you had to refactor legacy code to improve performance or maintainability.

Technical Interview

Review and discuss the take-home project, focusing on code quality, documentation, and design decisions.

What to demonstrate

  • Review and discuss the take-home project
  • Focusing on code quality, documentation, and design decisions

How to prepare

  • Answer aloud and timed: How would you design a microservice to handle real-time data from an external API and trigger a payout event?
  • Answer aloud and timed: What is your approach to containerizing a scientific engine using Docker for production deployment?

Where candidates lose points

Prioritize Code Style

As seen in previous feedback, a candidate's "coding style" can be a deciding factor. Ensure your submission is clean, readable, and well-documented.

Be Ready for Code Reviews

The technical interview is essentially a collaborative code review. Don't be defensive; treat it as an opportunity to show how you handle feedback.

Understand the Business

Research parametric insurance before your first interview. Showing you understand why the company exists will set you apart.

Practice Your French and English

Since the role requires fluency in both, be prepared to switch between them comfortably during the interview.

Questions & practice

How would you profile and optimize a piece of slow-running Python code that performs heavy mathematical calcul

Medium · Technical and Scientific Coding

How would you profile and optimize a piece of slow-running Python code that performs heavy mathematical calculations?

Approach

  1. Restate the input: its shape, its size, and what is guaranteed about it.
  2. Name the brute-force solution and its complexity before improving on it.
  3. Choose the data structure from the access pattern, not from familiarity.
  4. State the target complexity and say which constraint rules the naive version out.

Follow-up

  • How does this change if the input no longer fits in memory?
  • What is the worst case, and how likely is it on real data?

Explain the trade-offs between functional and object-oriented programming patterns in the context of a scienti

Medium · Technical and Scientific Coding

Explain the trade-offs between functional and object-oriented programming patterns in the context of a scientific simulation.

Approach

  1. Restate the input: its shape, its size, and what is guaranteed about it.
  2. Name the brute-force solution and its complexity before improving on it.
  3. Choose the data structure from the access pattern, not from familiarity.
  4. State the target complexity and say which constraint rules the naive version out.

Follow-up

  • How does this change if the input no longer fits in memory?
  • What is the worst case, and how likely is it on real data?

Describe a situation where you had to refactor legacy code to improve performance or maintainability.

Medium · Technical and Scientific Coding

Approach

  1. Restate the input: its shape, its size, and what is guaranteed about it.
  2. Name the brute-force solution and its complexity before improving on it.
  3. Choose the data structure from the access pattern, not from familiarity.
  4. State the target complexity and say which constraint rules the naive version out.

Follow-up

  • How does this change if the input no longer fits in memory?
  • What is the worst case, and how likely is it on real data?

Denormalise tenant onto revisions and backfill it live

Hard · denormalisation · online migration · backfill

resource_revision (revision_id, resource_id, version, actor_user_id, change_kind, patch, request_id, created_at) has 400M rows and no tenant column; tenant_id lives only on resource. Two reads need it: a tenant-scoped audit feed ordered by created_at DESC, and an offboarding purge. Both join back to resource today. Justify adding tenant_id to resource_revision against those two reads, name the anomaly the copy introduces and the constraint that prevents it, then give the ordered migration for a live table taking 1.2k writes/second — the lock each step takes, how the backfill is batched, and where each step stops being reversible. PostgreSQL 16.

Approach

  1. Justify from the access path rather than from taste. Without the column, the audit feed either scans resource_revision by created_at and discards other tenants' rows, or resolves the tenant's resource_ids first and probes with them — both proportional to the tenant's whole history rather than to one page. With (tenant_id, created_at DESC, revision_id DESC) it is a seek that stops at 50 rows, and the purge becomes a ranged delete instead of a join.
  2. Name the cost exactly: a second copy of a fact can disagree with the first. Make the disagreement unwritable rather than documented — add UNIQUE (resource_id, tenant_id) on resource so it can serve as a foreign-key target, then FOREIGN KEY (resource_id, tenant_id) REFERENCES resource (resource_id, tenant_id) on the revision table. A revision can then only ever carry its parent's tenant.
  3. Step one, expand: ALTER TABLE resource_revision ADD COLUMN tenant_id BIGINT NULL, with no default, so it is a catalogue change and no rewrite. It still needs ACCESS EXCLUSIVE for an instant, and that instant queues behind the longest open transaction on the table while every later query queues behind it — set lock_timeout to 2s and retry rather than wait.
  4. Step two, dual-write: deploy the writer that populates tenant_id on every new revision while reads still use the join. Reversible by redeploying the previous build, because nothing reads the column yet.

Follow-up

  • The backfill is half finished and a rollback is required. What state is the table in, and what does the previous build do with a half-populated column?
  • How do you verify the backfill actually finished, given rows are still being inserted while it runs?

Keep soft-deleted accounts from blocking re-registration

Easy · soft delete · unique constraints · engine differences

app_user holds user_id, tenant_id, email CITEXT, password_hash (NULL for SSO principals), email_verified_at, auth_version, status ('invited','active','suspended','deactivated'), created_at, updated_at, deleted_at. Two live accounts for one address inside a tenant must be impossible, but an address freed by a soft delete must be reusable, and the same tenant may delete and re-register it repeatedly. Write the uniqueness DDL for PostgreSQL 16, then the equivalent for MySQL 8 where partial indexes do not exist, and say what each permits once three deleted rows already hold that address.

Approach

  1. Start from what is actually unique: not (tenant_id, email), but (tenant_id, email) among live rows. PostgreSQL says that directly — CREATE UNIQUE INDEX app_user_live_email ON app_user (tenant_id, email) WHERE deleted_at IS NULL. A full constraint over the same two columns burns the address permanently the first time someone deletes an account.
  2. Keep case-insensitivity in the type or the index, never in the application: CITEXT as given, or UNIQUE (tenant_id, lower(email)) as an expression index where the extension is unavailable. A case-sensitive unique column is exactly how two accounts for one human appear.
  3. For MySQL 8 the predicate has to move inside the key: add a discriminator column that is a constant 0 while the row is live and is set to user_id on delete, with UNIQUE (tenant_id, email, deleted_marker). Live rows share the constant and still collide; deleted rows differ from each other and stop colliding.
  4. State the NULL variant and its dependency: leaving the marker NULL for deleted rows also works, because a unique index treats NULLs as distinct — true in MySQL, and true in PostgreSQL only under the default NULLS DISTINCT, which PostgreSQL 15 lets you reverse. Check the polarity against the three existing deleted rows: constant-on-live is what preserves the collision you want, and reversing it silently admits duplicate live accounts.

Follow-up

  • A deleted account re-registers with the same address the next day. Do the old resource rows follow the new user_id, and how does the API keep the two principals apart?
  • How do you honour an erasure request while resource_revision.actor_user_id still references this table?

How would you design a microservice to handle real-time data from an external API and trigger a payout event?

Medium · System Design and Architecture

Approach

  1. Fix the scope first: who calls this, how often, and what they do when it fails.
  2. Name the read and write paths separately; they rarely have the same bottleneck.
  3. Choose a partition key and say what query it makes expensive.
  4. State the consistency you need, and where you are willing to be stale.

Follow-up

  • What breaks first when traffic grows ten times?
  • How does this behave when that dependency is down for an hour?

What is your approach to containerizing a scientific engine using Docker for production deployment?

Medium · System Design and Architecture

Approach

  1. Fix the scope first: who calls this, how often, and what they do when it fails.
  2. Name the read and write paths separately; they rarely have the same bottleneck.
  3. Choose a partition key and say what query it makes expensive.
  4. State the consistency you need, and where you are willing to be stale.

Follow-up

  • What breaks first when traffic grows ten times?
  • How does this behave when that dependency is down for an hour?

How do you ensure your CI/CD pipelines are robust enough to handle frequent updates to risk models?

Medium · System Design and Architecture

Approach

  1. Fix the scope first: who calls this, how often, and what they do when it fails.
  2. Name the read and write paths separately; they rarely have the same bottleneck.
  3. Choose a partition key and say what query it makes expensive.
  4. State the consistency you need, and where you are willing to be stale.

Follow-up

  • What breaks first when traffic grows ten times?
  • How does this behave when that dependency is down for an hour?

p99 jumped on one listing filter while p50 stayed flat

Medium · query planning · composite index · percentiles

After a release that added an owner_user_id filter to the resource listing, p99 rose from 90 ms to 1.9 s while p50 stayed at 40 ms. Traffic and row counts are unchanged. resource carries the index (tenant_id, status, updated_at DESC, resource_id DESC). The new query filters tenant_id and owner_user_id, orders by updated_at DESC, resource_id DESC, and takes 20 rows. On PostgreSQL, explain the shape of the regression, prove it from a query plan, and give the index you would add.

Approach

  1. Start from the shape. A flat p50 with a moved p99 means a subset of requests changed cost, not all of them, so the first job is naming the subset. Bucket the endpoint's latency by the tenant's row count; the natural hypothesis is that large tenants are a small share of requests and all of the tail.
  2. Get the plan for the new query on a large tenant with EXPLAIN (ANALYZE, BUFFERS). Expect an index scan over the tenant's range, a filter discarding most of it, then a Sort feeding the Limit, possibly reporting Sort Method: external merge Disk. Read actual rows on the scan node, not estimated.
  3. Explain why the existing index cannot serve it. A composite B-tree is seekable only as a left prefix, and with no equality predicate on status the scan cannot treat updated_at as an ordering, because rows in the tenant's range are ordered by status first. Everything matching must be read and sorted before LIMIT 20 can apply, so a tenant with 400,000 rows pays 400,000 rows to return 20.
  4. Add (tenant_id, owner_user_id, updated_at DESC, resource_id DESC). Equality on the first two columns leaves the index ordered by updated_at within that pair, so the plan becomes an index scan that stops after 20 rows with no Sort node. PostgreSQL can scan a B-tree backwards, so the DESC markers matter only if the two sort columns ever disagree in direction; keeping them explicit documents the order the keyset cursor depends on.

Follow-up

  • The endpoint paginates with OFFSET. What does page 500 cost with your index, and what does the keyset version cost?
  • How would you have caught this before release, given that a 10,000-row seed database produces the same plan shape at an unnoticeable cost?

Suggested practice checklist

Built from the rounds and topics Descartes Underwriting candidates report.

Day 1 — Map the Descartes Underwriting loop

  • Write out the reported sequence: Initial Discovery Calls, Take-Home Project, Technical Interview.
  • For each round, write one sentence on what it is judging, from the description above, and mark the one you are least ready for.

Deliverable: A one-page map of the 3 reported rounds, with the weakest marked.

Day 2 — Work Python

  • Spend the session on Python, which Descartes Underwriting candidates report being tested on.
  • Write one worked example in Python and time yourself on it.

Deliverable: One timed worked example in Python.

Day 3 — Work Time Profiling

  • Spend the session on Time Profiling, which Descartes Underwriting candidates report being tested on.
  • Write one worked example in Time Profiling and time yourself on it.

Deliverable: One timed worked example in Time Profiling.

Day 4 — Work Python Performance Optimization

  • Spend the session on Python Performance Optimization, which Descartes Underwriting candidates report being tested on.
  • Write one worked example in Python Performance Optimization and time yourself on it.

Deliverable: One timed worked example in Python Performance Optimization.

Day 5 — Answer out loud: Technical and Scientific Coding

  • Answer aloud, timed: How would you profile and optimize a piece of slow-running Python code that performs heavy mathematical calculations?
  • Answer aloud, timed: Explain the trade-offs between functional and object-oriented programming patterns in the context of a scientific simulation.

Deliverable: Spoken answers to 2 reported Technical and Scientific Coding question(s), under time.

Day 6 — Answer out loud: System Design and Architecture

  • Answer aloud, timed: How would you design a microservice to handle real-time data from an external API and trigger a payout event?
  • Answer aloud, timed: What is your approach to containerizing a scientific engine using Docker for production deployment?

Deliverable: Spoken answers to 2 reported System Design and Architecture question(s), under time.

Day 7 — Answer out loud: Behavioral and Cultural Fit

  • Answer aloud, timed: How do you balance the need for rigorous, slow-moving insurance standards with the fast-paced nature of a tech-first company?
  • Answer aloud, timed: Tell us about a time you had to mentor a junior engineer—how did you approach the feedback process?

Deliverable: Spoken answers to 2 reported Behavioral and Cultural Fit question(s), under time.

Behavioural preparation

Behavioural rounds judge the decision you made and what it cost.

How do you handle large-scale data ingestion and ensure the integrity of the data being fed into a risk model?

Medium · Technical and Scientific Coding

Approach

  1. Pick a story where you made the decision, not one where you watched it.
  2. State the situation in two sentences and spend the rest on the reasoning.
  3. Give the blast radius: what could have broken, and what you measured.
  4. Name the disagreement and how you resolved it with evidence.

Follow-up

  • What would you do differently if you ran that again?
  • How did you know your change caused the improvement?

How do you balance the need for rigorous, slow-moving insurance standards with the fast-paced nature of a tech

Medium · Behavioral and Cultural Fit

How do you balance the need for rigorous, slow-moving insurance standards with the fast-paced nature of a tech-first company?

Approach

  1. Pick a story where you made the decision, not one where you watched it.
  2. State the situation in two sentences and spend the rest on the reasoning.
  3. Give the blast radius: what could have broken, and what you measured.
  4. Name the disagreement and how you resolved it with evidence.

Follow-up

  • What would you do differently if you ran that again?
  • How did you know your change caused the improvement?

Tell us about a time you had to mentor a junior engineer—how did you approach the feedback process?

Medium · Behavioral and Cultural Fit

Approach

  1. Pick a story where you made the decision, not one where you watched it.
  2. State the situation in two sentences and spend the rest on the reasoning.
  3. Give the blast radius: what could have broken, and what you measured.
  4. Name the disagreement and how you resolved it with evidence.

Follow-up

  • What would you do differently if you ran that again?
  • How did you know your change caused the improvement?

Why are you interested in applying your engineering skills to the climate and insurance sector?

Medium · Behavioral and Cultural Fit

Approach

  1. Pick a story where you made the decision, not one where you watched it.
  2. State the situation in two sentences and spend the rest on the reasoning.
  3. Give the blast radius: what could have broken, and what you measured.
  4. Name the disagreement and how you resolved it with evidence.

Follow-up

  • What would you do differently if you ran that again?
  • How did you know your change caused the improvement?