Interview guides · Zelis
Zelis Software Engineer Interview Guide
Prepare for Zelis SWE interviews with tested payment reconciliation, retry and audit decisions, a production incident walkthrough and a seven-day practice plan.
The 60-second brief
Zelis coordinates healthcare payments and remittance information. Use the three original worked cases below to prepare concrete integration and recovery answers.
Match application, platform or security depth to your actual requisition. The .NET vacancy reviewed is filled; this guide does not establish a universal stack.
Focus areas
- Reconcile tenant-scoped payment and remittance records
- Preserve operation identity across unknown outcomes
- Contain and repair a projection without moving funds
Overview
A healthcare payment workflow can look healthy while a provider still cannot explain a deposit. The payment record, remittance details and portal projection may arrive through different paths. An engineer needs to distinguish a delayed view from an absent payment, a duplicate event and a genuine financial mismatch before choosing a repair. This Zelis software engineer guide supplies three complete practice answers: a runnable reconciliation function, an uncertain-payment design trace and a production-incident walkthrough. All records, amounts, operation IDs and incidents are fictional. They are not Zelis customer data, internal architecture or leaked interview questions.
For unfamiliar-code investigation, use the Stripe Bug Squash guide. This verified practice resource supplies a transferable debugging workflow; it does not establish a Zelis round.
Zelis preparation checkpoints
Preparation checkpoints, not a verified universal Zelis loop. The retained self-referential source does not independently substantiate candidate reports; confirm stages, timing and tools with recruiting.
Initial Screening
Confirm team, scope, stages, duration and tools. Explain your real integration or operations contribution.
What to demonstrate
- Role-specific preparation and truthful ownership
- Explain assumptions, evidence and the next discriminating check.
How to prepare
- Ask which language and environment apply.
- Explain payment, remittance and settlement as separate concepts.
Technical Evaluations
Use the worked reconciliation and payment-retry exercises to defend exact identities, states and recovery decisions.
What to demonstrate
- Checkable contracts and failure handling
- Explain assumptions, evidence and the next discriminating check.
How to prepare
- Run the sample and inspect all five outcomes.
- Trace a lost response without creating a second payment identity.
Behavioral Rounds
Prepare a real incident and disagreement story with evidence, personal responsibility and remaining uncertainty.
What to demonstrate
- Clear contributions and candid reflection
- Explain assumptions, evidence and the next discriminating check.
How to prepare
- Use the fictional incident to rehearse technical follow-ups only.
- Explain one warning you acted on and one decision you would change.
Where candidates lose points
Equating a report match with settlement
The local function compares snapshots; it does not confirm a remote transfer or settlement.
Retrying with a new payment identity
A lost response can hide an accepted effect. Preserve the logical operation and reconcile through the documented provider contract.
Repairing funds for a projection defect
Verify authoritative state and repair the derived view. A duplicate remittance row does not authorize a compensating transfer.
Questions & practice
Diff a projection against the primary without per-row point reads
Hard · reconciliation · range hashing · throttling
The listing projection has drifted and some rows show a stale version. The primary holds 40,000,000 resource rows across 12,000 tenants while serving 1,200 writes and 14,000 reads per second. The obvious repair, reading each resource row and comparing its version against the projection, is correct and would eventually finish. Explain precisely why it is unacceptable here, then give a diff that finds the differing rows, state its complexity, and make it safe to run against a live primary. Replication lag is usually under 100 ms and is not bounded.
Approach
- Quantify the naive cost rather than calling it slow: 40,000,000 point reads at even 0.5 ms each is over five hours serialised, and the only lever is concurrency, which is exactly what you cannot spend. The primary's pool is sized for the write path, and 40,000,000 random reads evict the buffer cache that sustains the 85 percent cache hit rate, so the audit degrades the system it is auditing.
- Replace random access with one ordered pass per side. Both sides can be read in (tenant_id, resource_id) order, which is a sequential scan on each and a merge join in O(n) time and O(1) memory. For a dense diff that is the whole answer, and it reads the primary once instead of 40,000,000 times.
- For the expected sparse case, compare range hashes instead of rows: partition the key space, compute per range an order-independent aggregate over hash(resource_id, version), compare aggregates, and descend only into ranges that differ. With d differing rows and branching factor B, at most d ranges mismatch per level, so the drill-down examines O(d log_B(n/d)) ranges and reads full rows only in mismatching leaves.
- Aggregate with a sum modulo 2^64 or a multiset hash, never XOR. XOR is order-independent but self-cancelling, so two rows wrong in the same way, or a row duplicated on one side, leave the range aggregate matching and the range is declared clean.
Follow-up
- The diff reports 900 stale rows. How do you decide between patching those rows and rebuilding the projection from resource_revision?
- Same job, but the projection lives in a search index that cannot be scanned in key order. What changes?
Identify the heaviest tenants in a five-minute window under memory pressure
Medium · top-k · heavy hitters · streaming
The edge service handles about 3,000 requests per second across roughly 50,000 tenants, peaking near 9,000. Expose the 50 heaviest tenants by request count over the trailing five minutes so limits can be tightened before one tenant's backfill starves the fleet. You may not retain five minutes of raw records. Give the exact solution and its memory, then the bounded-memory approximation with its error stated as a formula, and say which you would ship and at what tenant cardinality that choice changes.
Approach
- Do the exact version first, because it is affordable at this cardinality: a ring of 300 one-second counters per tenant, advanced lazily, is 1,200 bytes of counters per tenant and roughly 60 to 90 MB for 50,000 tenants with overhead. Carry a running total and subtract the bucket you overwrite so a window read is O(1) rather than 300 adds.
- Extract the top 50 with a size-k min-heap over the tenant sums: O(d log k) for d tenants, against O(d log d) to sort them all. Maintaining the heap continuously instead of on query requires a tenant-to-heap-index map, because incrementing a count already inside the heap means sifting from a known position, and without that map you rebuild the heap on every request.
- State the approximation precisely rather than gesturing at sketches. Misra-Gries with m counters retains every item whose true count exceeds N/(m+1), and each retained count underestimates the truth by at most N/(m+1). With m = 1,000 and N = 900,000 requests in the window the error is roughly 900 requests, which is fine for spotting a tenant sending 50,000 and useless for ranking two tenants 200 apart.
- Say what breaks when the window slides: Misra-Gries and Space-Saving are insert-only and cannot be decremented as records age out. The workable construction is one summary per sub-window, say ten seconds, with 30 summaries merged at query time, and the merged error is the sum of the per-summary errors, so the bound degrades linearly in the number of sub-windows.
Follow-up
- The heaviest tenant is heavy because of one export job rather than user traffic. Should the limiter treat those as the same tenant?
- Two tenants sit tied at the boundary of the top 50. Does your answer flap, and does the flapping matter?
Merge partitioned event streams into one ordered feed with bounded lateness
Hard · k-way merge · watermarks · out-of-order streams
The read-model service consumes 64 log partitions carrying about 4,000 events per second in total. Each partition is ordered within itself, but partitions drift by up to 30 seconds, and the activity feed must present a tenant's events in occurred_at order. Produce the merge. State its complexity, the buffer it requires in events and in bytes, what happens when one partition is idle, and what you do with an event that arrives after you have already emitted its position. Payloads average 1 KB.
Approach
- Merge with a min-heap over the 64 partition heads keyed on (occurred_at, event_id): O(log P) per event and O(n log P) overall. The tie-break on event_id is what makes the output deterministic when two partitions carry the same millisecond, which matters because the feed is paginated and a non-deterministic order reorders pages under the reader.
- Emitting the heap head is only correct once every partition has produced everything up to that timestamp, so the emit condition is a watermark: the minimum across partitions of the highest occurred_at seen, less the allowed lateness. Events are held until the watermark passes them, which is what turns individually ordered streams into a jointly ordered one.
- Size the buffer from the lateness rather than guessing: 4,000 events per second times 30 seconds is 120,000 buffered events, and at 1 KB each about 120 MB of heap. That number is the real price of the ordering guarantee and belongs in front of whoever asked for it.
- Handle the idle partition explicitly, because it fails the feed rather than corrupting it: a partition with no traffic never advances its own maximum, so the watermark freezes and output stops entirely. Either every partition emits a periodic idle marker carrying the broker's current time, or the watermark falls back to wall clock for a partition silent beyond a threshold.
Follow-up
- The lateness budget is raised to five minutes. What is the new buffer, and what besides memory changes?
- The consumer restarts. Where does it resume from, and what does the feed look like for the first 30 seconds?
Reconcile payment and remittance snapshots
Medium · reconciliation · operation-identity · recovery
Use tenant-scoped payment and event identities, integer USD cents and trusted snapshots. Return five ordered outcome rows; duplicate delivery must not inflate P1.
Approach
- Define identities, authority and expected output.
- Use the supplied trace to show the failing boundary.
- Separate executed checks from proposed production mechanisms.
Worked solution (40 min)
- Original coding exercise. Payments contain
(tenant, payment_id, provider, currency, cents). Remittances contain(tenant, event_id, payment_id, provider, currency, cents). Compare the two snapshots and return a deterministic report without modifying the inputs or initiating transfers. Use integer USD cents from zero through 10^12 for each input row. Reject booleans, negative amounts, floating-point values, unsupported currencies and empty identifiers. Identifiers must already be canonicalized by the ingestion contract. A repeated payment identity must have the same payload. A repeated remittance event identity must reference the same payment and payload, or reject the batch. Deduplicate only by(tenant, event_id), then sum distinct remittance rows for(tenant, payment_id). Two claims can legitimately contribute separate rows to one payment. Identical payment IDs in different tenants must not merge. Provider mismatch takes precedence over amount comparison, because equal cents do not establish the correct recipient. - Sample input and exact report The executable sample has T1/P1 paid 10000 cents with two remittance rows of 6000 and 4000; the second row is delivered twice under the same event ID. T1/P2 is paid 8000 but remits 7900. T1/P3 has a 2500 payment and no remittance. T1/P4 has a 900 remittance but no payment. T2/P1 independently has a matching 700 payment. | Tenant/payment | Report | Expected cents | Remitted cents | | --- | --- | --- | --- | | T1/P1 | MATCH | 10000 | 10000 | | T1/P2 | AMOUNT_MISMATCH | 8000 | 7900 | | T1/P3 | MISSING_REMIT | 2500 | 0 | | T1/P4 | MISSING_PAYMENT | None | 900 | | T2/P1 | MATCH | 700 | 700 | This table is a snapshot report. “Missing” means absent from these supplied inputs, not proof that a payment never occurred. Capture source versions or a consistent cutoff before comparing live feeds; otherwise one feed’s lag can create a temporary mismatch.
- Complete runnable Python The complete runnable function and sample are in this exercise’s code panel. The function uses local dictionaries and lists; it performs no database or provider calls. Expected average processing is O(P + R + K log K), where P and R are input row counts and K is the number of distinct payment keys. Sorting gives deterministic output. Memory grows with accepted rows and identities; this is not a bounded-memory solution for an unbounded stream.
- What the tests establish The local run passed 1018 checks across 250 constructed cases. Expected statuses were chosen before generating and shuffling the input rows. The checks cover split remittance amounts, duplicates, tenant separation, identity mismatch, one-sided records, iterator inputs and input preservation. Focused checks reject conflicting identities and invalid amounts; two different event IDs remain distinct. This does not detect an economic duplicate reissued under a new event ID. It does not authenticate a tenant, parse an 835 message, handle fees or reversals, establish settlement or enforce database durability. Those require separate contracts. In a SQL implementation, deduplicate first and aggregate each side before joining; joining raw payment and remittance rows can multiply totals.
from collections import defaultdict
def reconcile(payments, remittances):
"""Fictional USD snapshot. Reports differences; never moves money."""
def text(value):
if not isinstance(value, str) or not value.strip():
raise ValueError('nonempty identifiers required')
return value
def amount(value):
if type(value) is not int or not 0 <= value <= 10**12:
raise ValueError('integer cents from 0 through 10**12 required')
return value
def identity(tenant, payment, provider, currency, cents):
if currency != 'USD':
raise ValueError('this fixture supports USD only')
return ((text(tenant), text(payment)),
(text(provider), currency, amount(cents)))
pay = {}
for tenant, payment, provider, currency, cents in payments:
key, payload = identity(tenant, payment, provider, currency, cents)
if key in pay and pay[key] != payload:
raise ValueError('conflicting payment identity')
pay[key] = payload
seen, rows = {}, defaultdict(list)
for tenant, event, payment, provider, currency, cents in remittances:
key, payload = identity(tenant, payment, provider, currency, cents)
event_key = (key[0], text(event))
event_payload = (key[1],) + payload
if event_key in seen:
if seen[event_key] != event_payload:
raise ValueError('conflicting remittance event')
continue
seen[event_key] = event_payload
rows[key].append(payload)
result = []
for key in sorted(pay.keys() | rows.keys()):
expected = pay.get(key)
remits = rows.get(key, [])
actual = sum(row[2] for row in remits)
if expected is None:
status = 'MISSING_PAYMENT'
elif not remits:
status = 'MISSING_REMIT'
elif any(row[:2] != expected[:2] for row in remits):
status = 'IDENTITY_MISMATCH'
elif actual != expected[2]:
status = 'AMOUNT_MISMATCH'
else:
status = 'MATCH'
result.append((key[0], key[1], status,
None if expected is None else expected[2], actual))
return result
if __name__ == '__main__':
payments = [('T1', 'P1', 'V1', 'USD', 10000),
('T1', 'P2', 'V1', 'USD', 8000),
('T1', 'P3', 'V2', 'USD', 2500),
('T2', 'P1', 'V9', 'USD', 700)]
remits = [('T1', 'E1', 'P1', 'V1', 'USD', 6000),
('T1', 'E2', 'P1', 'V1', 'USD', 4000),
('T1', 'E2', 'P1', 'V1', 'USD', 4000),
('T1', 'E3', 'P2', 'V1', 'USD', 7900),
('T1', 'E4', 'P4', 'V2', 'USD', 900),
('T2', 'E1', 'P1', 'V9', 'USD', 700)]
for row in reconcile(payments, remits):
print(row)
Expected result: Coding: five sample rows exactly; 1018 local checks. Design: retain unknown outcome after lost response and reconcile by actual provider contract. Incident: repeated E2 contributes extra 4000 only if counted twice; repair projection, not funds.
Verify it yourself
- Expected sample: MATCH 10000, mismatch 8000/7900, missing remit 2500, missing payment 900, separate T2/P1 MATCH 700.
- Repeated event identity with conflicting payload rejects the batch.
- No fixture contacts a provider, authenticates a tenant or moves money.
Follow-up
- What if the source snapshots have different cutoffs?
- What changes when an economic duplicate gets a new event ID?
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
- 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.
- 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.
- 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.
- 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?
Find version gaps and relay lag with window functions
Medium · window functions · event tables · frame semantics
outbox_event holds event_id, aggregate_type, aggregate_id, aggregate_version, event_type, payload, status ('pending','published','dead'), attempts, created_at, published_at. A projection is missing rows and you must decide whether the relay skipped events or the consumer dropped them. Write three queries over the last seven days: one listing every aggregate_id whose published aggregate_version sequence has a hole, one giving per-day counts with a running total, and one returning the newest published event per aggregate. For each, say where the window function is evaluated relative to WHERE and LIMIT. PostgreSQL 16.
Approach
- Gaps: compute lead(aggregate_version) OVER (PARTITION BY aggregate_id ORDER BY aggregate_version) in a subquery, then filter next_version <> aggregate_version + 1 in the outer query. Window functions are evaluated after WHERE, GROUP BY and HAVING and before the outer ORDER BY and LIMIT, so the predicate cannot sit in the same WHERE clause and PostgreSQL 16 has no QUALIFY.
- Say what the seven-day filter does to the answer: it truncates every partition, so the first row per aggregate has no predecessor inside the window and a hole spanning the boundary is invisible. Widen the window, or join to resource.version as the authority for the true maximum.
- Running total: SELECT date_trunc('day', created_at) AS d, count() AS n, sum(count()) OVER (ORDER BY date_trunc('day', created_at) ROWS UNBOUNDED PRECEDING). An aggregate inside a window call is legal because grouping runs before windowing. The grouping key is unique per row here so ROWS and RANGE agree, but write the frame anyway — over ungrouped rows with tied timestamps the default RANGE frame pulls in every peer row and the total jumps.
- Newest per aggregate: DISTINCT ON (aggregate_id) ... ORDER BY aggregate_id, aggregate_version DESC is the cheap PostgreSQL-only form when an index matches that order; row_number() OVER (PARTITION BY aggregate_id ORDER BY aggregate_version DESC) = 1 is the portable form and needs a subquery for the same evaluation-order reason as the gap query.
Follow-up
- Relay failover redelivers events. Does a duplicate break the gap query, and how would you detect one from this table alone?
- Turn the gap check into a continuous monitor rather than a query someone runs after an incident. What does it watch?
How do you approach monitoring and alerting for a distributed production system?
Medium · Technical Domain & Operations
Approach
- Fix the scope first: who calls this, how often, and what they do when it fails.
- Name the read and write paths separately; they rarely have the same bottleneck.
- Choose a partition key and say what query it makes expensive.
- 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?
Explain your process for identifying and remediating a critical security vulnerability in an existing applicat
Medium · Technical Domain & Operations
Explain your process for identifying and remediating a critical security vulnerability in an existing application.
Approach
- Clarify what is being asked and what a complete answer contains.
- State your assumptions explicitly before working the problem.
- Say what you would check first and why it is the highest-information step.
- Work from the requirement backwards to the design.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
What strategies do you use to ensure high availability in a cloud-based environment?
Medium · Technical Domain & Operations
Approach
- Fix the scope first: who calls this, how often, and what they do when it fails.
- Name the read and write paths separately; they rarely have the same bottleneck.
- Choose a partition key and say what query it makes expensive.
- 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 balance the need for rapid deployment with the necessity of system stability?
Medium · Technical Domain & Operations
Approach
- Clarify what is being asked and what a complete answer contains.
- State your assumptions explicitly before working the problem.
- Say what you would check first and why it is the highest-information step.
- Work from the requirement backwards to the design.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
Walk me through the design of a scalable data pipeline for analytics.
Medium · Problem Solving & System Architecture
Approach
- Fix the scope first: who calls this, how often, and what they do when it fails.
- Name the read and write paths separately; they rarely have the same bottleneck.
- Choose a partition key and say what query it makes expensive.
- 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 would you redesign a legacy component to improve its performance and maintainability?
Medium · Problem Solving & System Architecture
Approach
- Fix the scope first: who calls this, how often, and what they do when it fails.
- Name the read and write paths separately; they rarely have the same bottleneck.
- Choose a partition key and say what query it makes expensive.
- 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 prioritize technical debt against new feature development?
Medium · Problem Solving & System Architecture
Approach
- Fix the scope first: who calls this, how often, and what they do when it fails.
- Name the read and write paths separately; they rarely have the same bottleneck.
- Choose a partition key and say what query it makes expensive.
- 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?
Resolve a lost payment response without duplicating the effect
Medium · reconciliation · operation-identity · recovery
Operation O17 for 10000 USD cents was accepted by a fictional provider, but the response was lost. Trace record, dispatch, timeout, repeated delivery and late callback with precise recovery authority.
Approach
- Define identities, authority and expected output.
- Use the supplied trace to show the failing boundary.
- Separate executed checks from proposed production mechanisms.
Worked solution (35 min)
- Original design exercise. A tenant requests payment operation O17 for provider V1 and 10000 USD cents. The external provider accepts it, but the worker loses the response. A queue redelivers the request. What should the service do? Start with a durable operation record keyed by tenant and logical operation ID. Store the canonical amount, provider, currency and relevant request fields. A repeated ID with a different payload is a conflict, not a second payment. The tenant comes from authenticated server context, and authorization must cover the provider and operation. Create the operation and an outbound-work record atomically. The dispatcher may deliver that work more than once. It sends a stable provider idempotency identity when the actual provider supports that contract. Do not assume your local unique constraint makes the remote effect exactly once; there is no shared transaction spanning your database and a bank or processor.
- Resolve the trace without inventing success O17 moves from recorded to dispatched. After the timeout, retain an unknown outcome and record the attempt. Stop automatic creation of replacement payment identities. Reconcile using the provider’s documented lookup, retry or callback mechanism. An authoritative acceptance or settlement result updates the operation through a conditional state transition; remittance arrival is another correlated event, not the same state. Use a separate event identity to deduplicate callbacks, verify the referenced operation and append an audit record in the same transaction as the accepted state change. An older “processing” callback must not overwrite a newer authoritative settled state. State precedence must follow the provider’s contract, rather than a universal rule that “the latest timestamp wins.” For contrast, Stripe documents that repeated requests under an idempotency key return the original status and body, including a 500 response, and that keys can be pruned after at least 24 hours. These are Stripe-specific semantics, not Zelis’s integration contract. Read the actual provider’s retention, parameter-conflict and uncertain-outcome rules. Stripe idempotent requests. | Event | Required decision | Audit evidence | | --- | --- | --- | | O17 recorded | One canonical operation and outbound work | Authorized actor, request identity, version | | Response lost | Mark uncertain; retain same identity | Attempt time, provider correlation, error class | | Queue repeats O17 | Compare payload; reconcile/retry by contract | Duplicate delivery and chosen recovery path | | Authoritative settlement arrives | Conditional state update | Verified event identity and status provenance | | Old callback arrives afterward | Reject invalid regression | Current version and rejected transition | These steps are a proposed design. The pure reconciliation tests do not execute a durable outbox, callback authentication or processor integration. A production implementation needs crash-point and concurrent-delivery tests, expired-key recovery and an operator workflow for unresolved outcomes.
- Security, reliability and self-review A web-application firewall does not establish tenant authorization or payment correctness. Check object ownership at the server, restrict sensitive exports and use parameterized database queries. Keep operation IDs free of personal information. Audit who could authorize a status correction separately from who could inspect a report. Measure unresolved-outcome age, remittance lag, conflicting event payloads and reconciliation differences by tenant-safe scope. Distinguish queue depth from oldest-item age and processing success from confirmed business state. Bound retries and define who handles cases that cannot be resolved automatically. Use this editorial self-review rubric, not an alleged Zelis scoring sheet: contract names identities and money units; correctness handles duplicates and mismatches; recovery preserves uncertain outcomes; security states authority; communication separates evidence from assumptions. A strong answer demonstrates each with a fixture or trace. “Use microservices, retries and monitoring” leaves the important decisions unspecified.
- Role and evidence scope Zelis’s provider-payments page describes coordinating payments, remittance and communications, synchronizing payment data and supporting different delivery modalities. That makes integration correctness, reconciliation and operational recovery useful preparation themes. The product description does not prove a particular database, queue or hiring sequence. Zelis provider payments. The careers site emphasizes cross-disciplinary collaboration and its healthcare-financial mission. Ask recruiting for your team, language, stages, environment and tool rules. The reviewed senior .NET job URL is now marked filled; its earlier search excerpt should not be presented as a current opening or a requirement for every engineer. Zelis careers. The existing technical prompts remain available. Their source includes this guide itself, which is not independent verification of candidate reports. Treat the stages as preparation checkpoints, confirm the actual loop and select application, platform or security depth from your requisition.
Expected result: Coding: five sample rows exactly; 1018 local checks. Design: retain unknown outcome after lost response and reconcile by actual provider contract. Incident: repeated E2 contributes extra 4000 only if counted twice; repair projection, not funds.
Verify it yourself
- Expected sample: MATCH 10000, mismatch 8000/7900, missing remit 2500, missing payment 900, separate T2/P1 MATCH 700.
- Repeated event identity with conflicting payload rejects the batch.
- No fixture contacts a provider, authenticates a tenant or moves money.
Follow-up
- What if the source snapshots have different cutoffs?
- What changes when an economic duplicate gets a new event ID?
Describe a time you had to troubleshoot a complex, multi-layered production issue.
Medium · Problem Solving & System Architecture
Approach
- Establish what changed and when, before forming any theory.
- Pick a bisection that eliminates candidates whichever way it turns out.
- Check the instrumentation before believing the symptom.
- Separate the trigger from the cause; the deploy is rarely the bug.
Follow-up
- What would you look at first, and what would it rule out?
- How would you tell a cause from a coincidence here?
Repair a remittance projection without moving funds
Medium · reconciliation · operation-identity · recovery
A fictional portal overstates T1/P1 by 4000 cents after an importer change. Distinguish duplicate delivery from settlement, contain safely and verify the repaired projection.
Approach
- Define identities, authority and expected output.
- Use the supplied trace to show the failing boundary.
- Separate executed checks from proposed production mechanisms.
Worked solution (30 min)
- Fictional incident prompt. A provider reports that yesterday’s portal total exceeds the trusted payment snapshot by 4000 cents. The remittance importer recently changed. You are the on-call engineer; actual funds must remain untouched while you investigate. Separate customer communication, evidence collection and containment. Confirm the affected tenant, payment IDs, source cutoffs and meaning of each displayed status through authorized views. Preserve restricted evidence and avoid putting patient, bank or claim details in broad logs. A portal projection is not the settlement authority. A discriminating probe uses T1/P1’s sample: raw remittance amounts 6000 + 4000 + 4000 sum to 14000, while deduplicated events sum to 10000. Predict that the extra 4000 appears only when the repeated E2 delivery is counted. Then inspect whether the importer uses event identity or only a generated row ID. Compare adjacent tenants and older importer versions before concluding blast radius. If that hypothesis holds, pause the faulty projection updater or route reads to a verified safe view while keeping the payment execution path separate. Preserve the immutable source events. Patch event-key validation and conflict handling, rebuild the affected projection from trusted inputs at a known cutoff and compare the five expected report rows. Do not send a compensating transfer because a display is wrong. The recovery decision requires both corrected totals and a healthy subsequent observation window, plus explicit handling of conflicting event payloads. Track affected payment identities and whether totals were overstated; report unique-provider impact only if you actually measured it. A passed unit test is one piece of evidence, not proof that every historical projection is repaired. For a behavioral answer, use this only as a rehearsal structure. Tell a real incident from your own work, name your personal contribution and colleagues’ roles, explain the rejected hypothesis and say what remained uncertain. Do not claim the fictional payment incident as experience.
Expected result: Coding: five sample rows exactly; 1018 local checks. Design: retain unknown outcome after lost response and reconcile by actual provider contract. Incident: repeated E2 contributes extra 4000 only if counted twice; repair projection, not funds.
Verify it yourself
- Expected sample: MATCH 10000, mismatch 8000/7900, missing remit 2500, missing payment 900, separate T2/P1 MATCH 700.
- Repeated event identity with conflicting payload rejects the batch.
- No fixture contacts a provider, authenticates a tenant or moves money.
Follow-up
- What if the source snapshots have different cutoffs?
- What changes when an economic duplicate gets a new event ID?
Suggested practice checklist
Use the new reconciliation, payment-state and incident answers first. Retained stream and SQL drills provide secondary role-specific practice.
Day 1 — Confirm the role and money contract
- Ask recruiting about language, scope, stages and tools.
- Distinguish payment, remittance and settlement.
Deliverable: A confirmed role map and identity contract.
Day 2 — Run the reconciliation sample
- Execute the complete code.
- Explain all five ordered report rows.
Deliverable: The exact five-row report.
Day 3 — Challenge duplicate and tenant handling
- Use a conflicting event payload and a second tenant.
- Explain new-ID economic duplicates and snapshot cutoff limits.
Deliverable: A boundary-check table.
Day 4 — Trace the unknown payment
- Walk O17 through the lost response and repeated delivery.
- Separate durable local work from the provider effect.
Deliverable: An operation trace with current authority.
Day 5 — Contain the projection incident
- Reproduce 14000 raw versus 10000 deduplicated.
- Keep funds unchanged and rebuild at a trusted cutoff.
Deliverable: A repair and verification plan.
Day 6 — Explain a true production decision
- Rehearse a real incident or disagreement.
- Name your contribution, colleagues and uncertainty.
Deliverable: An honest evidence-backed story.
Day 7 — Run a changed-requirement mock
- Add currencies, reversals or source lag.
- State which original guarantees no longer apply.
Deliverable: A revised contract and next check.
Explain a true incident with evidence and ownership
Use the fictional incident only for rehearsal. For experience questions, explain what actually happened, your contribution, colleagues, rejected hypotheses, customer impact and remaining uncertainty.
Describe your experience with containerization and orchestration tools in a production setting.
Medium · Technical Domain & Operations
Approach
- Pick a story where you made the decision, not one where you watched it.
- State the situation in two sentences and spend the rest on the reasoning.
- Give the blast radius: what could have broken, and what you measured.
- 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?
Unblock an engineer without taking the keyboard
Easy · mentoring · leases · at-least-once
A teammate has spent two days on a job handler that occasionally writes duplicate rows. They are certain the queue is delivering twice by mistake. You suspect a lease expiring under a slow handler, so the job is running concurrently with itself. Describe how you have unblocked someone in this position: what you asked before offering a hypothesis, what you showed them rather than told them, and what you left them owning. Then say what you would do if their theory turned out to be the right one.
Approach
- Ask before diagnosing, and ask for things answerable from data they already have: the attempt count on the job rows that produced duplicates, the handler's observed duration against its lease expiry, and whether the duplicate rows share a natural key that a unique constraint could have caught.
- Teach the shape rather than the answer. A lease cannot distinguish a dead worker from a slow one, so a handler that outruns its lease is running twice by design, and deploys deliver the other half by killing handlers mid-run on every rollout. Both of their candidate theories produce identical duplicate rows, which is why the evidence has to come from timings rather than from argument.
- Hand over a checklist they execute: a natural key on every write the handler performs so the second copy collides rather than appends, the record of intent written before any external effect, a lease heartbeat while running, and the metric that shows it working.
- Keep ownership with them deliberately. Pair on the first write, then step back; if you finish it yourself you have closed one ticket and left the same person stuck on the next redelivery.
Follow-up
- How would you distinguish a genuine double-delivery from a lease expiry using only the data already stored?
- Their handler calls an external endpoint before recording that it did. What do you tell them to change first?
Argue against a design, lose, and commit anyway
Medium · disagreement · service boundaries · decision records
Describe a design you argued against and lost. State the failure you predicted as a named mechanism, not a feeling about complexity: two services that would need one transaction, a projection with no rebuild path, a write path with no idempotency key. Say what evidence you brought, what the decision maker weighed instead, and what you did after the decision was made: what you instrumented, what you wrote down, and whether the prediction came true. Five minutes.
Approach
- State the prediction in falsifiable form up front: the mechanism, the condition that triggers it, and the observable outcome. A prediction that cannot be checked also cannot be credited to you later.
- Show the evidence you had at the time and label each piece honestly as measured, analogous, or intuition. Keeping the intuition is fine; disguising it as data is the thing that erodes your standing in the next argument.
- Represent the opposing case at full strength, including the constraint you did not control: a fixed date, a team boundary, or the fact that the decision was cheap to reverse and yours was not.
- Make disagree-and-commit concrete. Name the artefact you left behind so the prediction could be settled without you: the alert and its threshold, the counter on the dashboard, the decision note that recorded the trade-off and the condition that would revisit it.
Follow-up
- What threshold on that alert would have proved you right, and did anyone ever look at it?
- If the same proposal arrived tomorrow with the same deadline, would you argue it the same way?