Garner health · Software Engineer
Updated · 2026-09-24

Garner health Software Engineer
Interview Guide

THE 60-SECOND BRIEF

As a Software Engineer at Garner Health, you play a critical role in transforming the healthcare economy by developing innovative software solutions that improve the quality and affordability of care for all. Your work directly impacts millions of patients, employers, and healthcare providers by enabling data-driven insights that guide better healthcare decisions. The position is not just about coding; it involves solving complex problems that have real-world implications, making it both challenging and rewarding.

If the loop includes an asynchronous take-home, treat it as a code review of you rather than as a puzzle: structure, what you chose to test, and what you wrote down about the constraint you were working under. Hold the stated time box and say what you would have done with more of it.

Garner health candidates report 3 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.

Authorise every identified read against current consentVersion clinical results instead of updating rows in placeModel bitemporal coverage and retroactive eligibility changes

36 min read

Practice 15 Software Engineer prompts
4Company bank questionsSnapshot · Sep 28, 2026 PT
1Candidate experiences ↗Read their reports
15Practice promptsAcross five skill areas
3With worked solutionsIncluded in the practice prompts

As a Software Engineer at Garner Health, you play a critical role in transforming the healthcare economy by developing innovative software solutions that improve the quality and affordability of care for all. Your work directly impacts millions of patients, employers, and healthcare providers by enabling data-driven insights that guide better healthcare decisions. The position is not just about coding; it involves solving complex problems that have real-world implications, making it both challenging and rewarding.

You will be part of a dynamic engineering team that tackles significant technical challenges, such as analyzing billions of medical records to rank healthcare providers across the country. This role is crucial in a rapidly growing company that emphasizes the integration of new technologies, including AI, to enhance operational efficiency and user outcomes. Your contributions will help shape healthcare solutions that are not only effective but also deeply aligned with the mission of Garner Health.

01

Initial Screening Call

reported

Before anything technical happens, someone has to decide which rung of the ladder your loop is calibrated to, and that decision sets the bar for every round after it. It comes from how you describe scope, not from your title, because titles do not convert cleanly between companies. The weak version of the answer is team size and years. The strong version names the largest change you shipped where nobody reviewed the design, what would have broken if you had been wrong, and what you were paged for. Get the level said out loud on this call, because the range and the loop both follow from it.

What to demonstrate

  • Whether the scope in your own account maps onto a level the team actually has an opening at, so a mismatch ends the process cheaply rather than after four interviewers have spent a day
  • Whether your title needs re-mapping: the same word describes very different amounts of independent decision-making at a twenty-person company and a ten-thousand-person one
  • Whether your compensation expectation can be filled at that level in the structure the role pays in, which is why the number gets asked for before any engineer is scheduled

How to prepare

  • Write down two changes from the last two years: the largest one you designed with nobody reviewing the design, and the largest one where someone more senior did. Lead with the first when scope comes up, and be ready to say which parts of the second were yours
  • Ask which level the loop is calibrated to and what changes at the level above it, then plan your weeks from that answer rather than from the posting
  • Settle a total-compensation range beforehand with the split named, base against bonus against equity and its vesting period, so a question about numbers gets a number instead of the word market
PracHub interview research ↗
02

Technical Interviews

reported

The same problem is scored by two different mechanisms depending on the format, and preparing for one does not cover the other. With a person watching, partial progress is visible and a hint is a correction you can absorb; silence is the expensive failure, because nobody can read a half-written function. With an automated grader there is no partial credit for what you were about to do, nobody to ask, and the worked examples in the prompt are the entire specification. Read them as a contract, down to whether an empty result should be an empty list or no output at all.

What to demonstrate

  • In a live session, whether your commentary tracks what your hands are doing, and whether a hint redirects you or gets defended against
  • In an automated one, whether you cover the cases the examples do not show, since the hidden cases are where the score moves
  • Whether you manage the clock on purpose: abandoning an approach that is not converging while there is still time to write something simpler that finishes

How to prepare

  • Have someone hand you a problem and feed you one deliberately wrong hint. Practise testing it against a concrete case instead of accepting or rejecting it on authority.
  • Do one timed run a week in a plain browser editor with autocomplete, linting and your own snippets switched off, which is closer to what these environments give you
  • For the automated format, write the harness before the solution: a main that feeds the worked examples plus an empty and a single-element case and prints expected against actual, so a wrong submission is caught by you first
PracHub interview research ↗
03

Behavioral Interviews

reported

Your first answer is not really what is scored. It buys the follow-up questions, and those decide the round. An interviewer with fifteen minutes takes one thread and pushes on it four or five times, so a story you can only tell at a single level of detail collapses under the third why. That is an argument for fewer stories known deeply rather than one prepared per prompt. Four or five pieces of work you can still explain down to the code you changed and the argument you had about it will cover nearly anything asked in this round.

What to demonstrate

  • Whether a story holds as the questioning moves from what you did to why that instead of the alternative, and then to what you would change knowing what you know now
  • Whether you can re-cut a project to answer the question actually asked rather than delivering a rehearsed block that answers an adjacent one
  • Whether your level of detail is chosen rather than habitual: going down to the schema when the question is about the data model, staying out of it when the question is about the person who disagreed with you

How to prepare

  • Pick four projects and write the chain out four levels deep for each: what you did, why that, why not the alternative, and what would have to be true for the alternative to have won. Where you cannot reach the fourth level, you have a placeholder rather than a story
  • Have someone ask why three times in a row on a single thread with nothing else added, and mark the point where you start repeating a sentence you already said. That point is where the interviewer stops learning anything
  • Build a one-page index instead of an answer bank: the common prompts in this round (disagreement, a failure that was yours, thin requirements, a deadline you missed, work you inherited) mapped to which of your four projects you would use for each, so the choosing is done now rather than while an interviewer waits
PracHub interview research ↗

1 candidate reports. Individual accounts describe a particular role and hiring cycle.

Software Engineer

Garner Health Software Engineer Interview Experience — Scaling an Appointment Booking System

Technical Screen

My Garner Health interview was a system design round. The first question was to design an appointment booking system where patients could search for doctors and book appointments. I discussed the core Patient, Doctor, and Appointment data models; how to query a doctor's available time slots; and how to book or cancel an appointment. The design also needed to prevent multiple patients from booking…

Read full experience

PracHub editorial advice for the preparation topics above.

01

Assuming admission, discharge and transfer messages arrive in the order the events happened.

Interface engines route by message type across separate queues and retry independently, so a discharge can land before the admission it closes and an update can land before the registration it modifies. Ordering has to come from the sender's event timestamp plus a per-encounter sequence, and the consumer has to apply out-of-order and late-arriving events correctly rather than rejecting them, because rejection turns a recoverable ordering issue into permanent data loss that nobody notices until a report is short.

02

Treating a medical record number or a member ID as a globally unique key and joining on it directly.

These identifiers are unique only within the authority that issued them. Two facilities in one network routinely have the same medical record number for different people, and member IDs get reissued when someone changes plans. Joining on the bare value merges two patients' records, which is the most damaging failure available in this domain, and it passes every test written against a single-facility fixture because the collision only appears once a second source is connected.

03

Listing technologies instead of trade-offs

Name the property the design needs first, such as ordered range scans, multi-entity transactions, cheap appends, or a predictable p99, then pick something that provides it and say what it gives up in exchange. Almost any component is defensible once you state the requirement it satisfies and the one it sacrifices.

04

Reading the constraints as preamble rather than as part of the problem

The bounds are usually there to eliminate the obvious approach: n up to 10^5 makes an O(n^2) scan roughly 10^10 operations, far outside any per-test time budget, and an input larger than memory rules out loading it at all. When a bound is not given, ask for it, then say out loud which approach it kills.

Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.

12 technical prompts3 include a worked solution

Write a function to calculate the Fibonacci sequence using recursion a…

medium
data structures and algorithms

Write a function to calculate the Fibonacci sequence using recursion and iteration.

Approach
  1. Restate the input: its shape, its size, and what is guaranteed about it.
  2. State the target complexity and say which constraint rules the naive version out.
  3. Name the brute-force solution and its complexity before improving on it.
Follow-up
  • How does this change if the input no longer fits in memory?
  • Which test case would catch an off-by-one here?

Discuss your approach to testing your code and ensuring its reliabilit…

medium
data structures and algorithms

Discuss your approach to testing your code and ensuring its reliability.

Approach
  1. State the target complexity and say which constraint rules the naive version out.
  2. Choose the data structure from the access pattern, not from familiarity.
  3. Restate the input: its shape, its size, and what is guaranteed about it.
Follow-up
  • What is the worst case, and how likely is it on real data?
  • How does this change if the input no longer fits in memory?

Solve a problem involving sorting a large dataset efficiently.

medium
data structures and algorithms

Solve a problem involving sorting a large dataset efficiently.

Approach
  1. Name the brute-force solution and its complexity before improving on it.
  2. State the target complexity and say which constraint rules the naive version out.
  3. Walk one small example through your approach before writing the whole thing.
Follow-up
  • What is the worst case, and how likely is it on real data?
  • How does this change if the input no longer fits in memory?

Create a function that checks if a string is a palindrome.

medium
data structures and algorithms

Create a function that checks if a string is a palindrome.

Approach
  1. State the target complexity and say which constraint rules the naive version out.
  2. Choose the data structure from the access pattern, not from familiarity.
  3. Restate the input: its shape, its size, and what is guaranteed about it.
Follow-up
  • What is the worst case, and how likely is it on real data?
  • How does this change if the input no longer fits in memory?

Reconstruct what the chart displayed at five million past instants

hardWorked solution
external sortas-of joinio bound

observation_result holds 200 million versions: observation_id, enterprise_person_id, filler_order_id, loinc_code, value_numeric, result_status, collected_ts, issued_ts, version, supersedes_observation_id. An incident review hands you 5 million queries of (enterprise_person_id, filler_order_id, loinc_code, as_of_instant). For each, return the version that was visible at that instant, meaning the one with the greatest issued_ts at or before it. Neither side fits in memory. The per-query scan is correct. Explain precisely why it is too slow, then give a plan with its complexity.

Approach
  1. Name the clock before naming an algorithm. Visibility is issued_ts, the release time. collected_ts is when the specimen was drawn and can precede release by hours, so ordering on it reports a correction as visible long before anyone could have seen it. No amount of index work rescues the wrong column.
  2. Be exact about why the naive plan fails, because the interviewer is testing whether you can tell arithmetic cost from I/O cost. Per query the chain scan is O(V_k) and the arithmetic is trivial, but 5 million independent lookups into a 200-million-row structure that does not fit in RAM is 5 million random reads. The job is bounded by seeks per query, not by comparisons, and buying a faster comparison changes nothing.
  3. Convert random access into sequential access. Hash-partition both sides on the chain key (enterprise_person_id, filler_order_id, loinc_code) into P shards sized to fit memory, sort each shard's versions by (chain key, issued_ts) and its queries by (chain key, as_of_instant), and sweep the pair in lockstep. Total O((V + Q) log(V + Q)) with external sort, replacing Q seeks with two sequential passes.
  4. Inside a chain the sweep is linear, not logarithmic, because queries are visited in ascending as_of order and the version pointer only moves forward: O(V_k + Q_k) per chain. Binary search per query is the better shape only when Q is small relative to V and the versions are already indexed and resident.
  5. Return the empty answer as a distinct outcome. A query whose as_of precedes the first issued_ts means nothing was displayed, which is not the same as the earliest value, and is frequently the exact fact the review is chasing.
  6. Return a version later marked entered_in_error if it was live at the instant asked about. Reconstructing the past means reporting what was on the screen, including what was wrong, and quietly substituting today's truth defeats the purpose of the exercise.
Worked solution 45 min
  1. Choose P so a shard fits in memory: at P equal to 256, each shard holds about 781,000 versions and about 19,500 queries.
  2. Hash-partition versions and queries on the chain key, writing both to shard files.
  3. Per shard, sort versions by (chain key, issued_ts) and queries by (chain key, as_of_instant).
  4. Sweep the two sorted streams together, advancing the version pointer while issued_ts is at or before as_of and answering from the last version passed.
  5. Emit an explicit empty answer when the pointer has not advanced past any version for that chain, and concatenate the shard outputs.
EXPECTED RESULTA chain with version 1 preliminary 5.2 issued 09:12, version 2 final 5.4 issued 11:40, version 3 corrected 7.1 issued 14:02 answers: as_of 08:00 returns nothing displayed, 10:00 returns 5.2 preliminary, 12:00 returns 5.4 final, 14:10 returns 7.1 corrected. If version 3 is marked entered_in_error at 18:00, the 14:10 query still returns 7.1, because that is what was on the screen. The whole job is two sequential passes over 200 million rows instead of 5 million seeks.
Follow-up
  • Read replicas lagged 40 seconds at the time. Does your answer describe what the clinician actually saw, and how would you bound the difference?
  • Serve the same question online for a single chart at a p99 under 50ms. What changes?
  • One partner changed its filler_order_id format mid-year, so the chain key is not stable. How does that appear in your output, and how do you detect it rather than returning empty answers?

For someone fluent in a dynamic language who has shipped real work but has never had to say what the runtime is doing underneath. The week is built on measuring and deliberately breaking things, because the questions that expose this background are the ones where the interviewer asks why a second time.

Small steps. Visible outcomes.0 / 7 completed
ONE WEEK · YOUR PACE

Prepare, practise & reflect

One practical outcome each day. Spend longer where you need it.

0 / 7 done
01Measure before reasoning
  • Take a slow piece of your own code, write down in advance where you believe the time goes, then profile it and record how wrong the guess was. The cost is usually an allocation you did not notice or an accidental quadratic membership test.
  • Replace one list membership test inside a loop with a set and measure at a thousand, ten thousand and a hundred thousand elements, confirming the shape of the curve rather than only that it got faster.
  • Write down the three quantities you can now measure instead of assert: wall time, peak memory, and call count for the function you suspected.

Deliverable: A before-and-after profile of real code plus a written note on the size of the gap between the guess and the measurement.

Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗
02References, copies, and the bugs they produce
  • Write the function with a mutable default argument, call it three times, and explain the accumulating result: the default is evaluated once when the function is defined, so every call shares one object.
  • Build a nested structure, take a shallow copy, mutate an inner element, and show that both views changed, because a shallow copy duplicates the container and not the elements. Then fix it with a deep copy and state the cost you just accepted.
  • Write two functions, one mutating its argument in place and one rebinding the local name, and predict the caller's view of each before running it. That single distinction produces most of the bugs that pass their tests.

Deliverable: Three small programs whose output you predicted correctly before running, each with a one-line statement of the rule underneath.

Practice prompt ↗Practice prompt ↗
03Types, once, in a language that checks them
  • Port one module you have already written, roughly a hundred lines, into a statically typed language, and record every place the compiler demanded an answer your original had left implicit: a value that can be absent, a numeric width, a case never handled.
  • Write the same signature in both languages and state what the static one guarantees before the program runs and what it does not, since it will not save you from a wrong algorithm or an index out of range.
  • Write the difference between an interface satisfied by declaration and one satisfied structurally, with one case each where the other approach would miss the mistake.

Deliverable: One module in two languages plus a list of the questions the type checker forced you to answer.

Practice prompt ↗Practice prompt ↗
04Concurrency, starting with what actually runs at the same time
  • Run the same CPU-bound function across four threads and four processes and measure both. Under the default CPython build the threaded version will not speed up, because only one thread executes bytecode at a time; the process version will. Check which build you are on first, since free-threaded builds remove that lock and change the result.
  • Then run a blocking I/O workload across four threads and measure it speeding up, because the interpreter releases that lock around blocking calls, which is why treating threads as useless is wrong as a general claim.
  • Build the lost update: two threads each incrementing a shared counter a hundred thousand times, and show a final value below the expected sum, because an increment is a load, an add and a store and the thread can be suspended between them. Fix it with a lock and then measure what the lock costs.

Deliverable: Three measurements, threads against processes on CPU work, threads on I/O work, and a demonstrated lost update, each with the mechanism written underneath.

Practice prompt ↗Practice prompt ↗Worked solution ↗
05Debugging as a procedure rather than an instinct
  • Work one real failure as a bisection: find a revision or an input size where it is good and one where it is bad, halve repeatedly, and state the two assumptions bisection needs, that the property changes exactly once across the range and that the test is reliable.
  • Minimise one failing input to the smallest version that still fails, and record how many rounds it took.
  • Keep a hypothesis log for one bug in three columns, what I believe, what would disprove it, what I observed, and stop yourself the first time you are about to change two things at once.

Deliverable: One bug worked to root cause with a written hypothesis log and a minimised reproducing input.

Practice prompt ↗Practice prompt ↗
06Tests that catch the bug you are about to write
  • Implement an LRU cache with a capacity bound, then write the three test cases that would catch an off-by-one in eviction: insert exactly capacity items and assert nothing was evicted, insert one more and assert the least recently used key is the one gone, and read an old key just before that insert so the eviction victim changes.
  • Add a property test comparing your implementation against a deliberately slow reference, an ordered list scanned linearly, over a few thousand random operation sequences, because a slow reference finds the cases you would not have thought to write.
  • Write one numeric test that fails under exact equality and passes with a tolerance, and state why the tolerance has to be relative rather than absolute once the magnitudes grow.

Deliverable: An LRU implementation with three boundary tests, one property test against a slow reference, and one tolerance-based numeric test.

Practice prompt ↗Practice prompt ↗
07Debug something broken, out loud
  • Have someone plant three defects in a two-hundred-line program, an off-by-one, a shared mutable state bug, and a wrong error-handling path, then find them while narrating, under a fixed rule: state the hypothesis before touching anything.
  • Time each one and record which tool found it, reading, a printed value, a debugger, or a test, because the question asked in interviews is how you would find it rather than what it was.
  • Write the sentence you will use when you do not yet know the cause, one that names the next measurement instead of offering a guess.

Deliverable: A recorded debugging session with time-to-find per defect and the method that found each.

Practice prompt ↗Practice prompt ↗Worked solution ↗

Expand any day for tasks and deliverables. Your progress is saved on this device.

Conflict answers where you were right and everyone came round are the weakest ones. Stronger: the evidence you went and collected, what would have changed your mind, and what you did in the weeks after the call went against you. Implementing a design you argued against, properly, is a specific and checkable behaviour.

How do you handle error management in your applications?

medium
behavioural and engineering judgement

How do you handle error management in your applications?

Approach
  1. Pick a story where you made the decision, not one where you watched it.
  2. Name the disagreement and how you resolved it with evidence.
  3. State the situation in two sentences and spend the rest on the reasoning.
Follow-up
  • What did you decide not to do, and why?
  • What would you do differently if you ran that again?

How do you handle feedback and criticism on your work?

medium
behavioural and engineering judgement

How do you handle feedback and criticism on your work?

Approach
  1. Name the disagreement and how you resolved it with evidence.
  2. Close with what you would do differently, concretely.
  3. Pick a story where you made the decision, not one where you watched it.
Follow-up
  • What would you do differently if you ran that again?
  • What did you decide not to do, and why?

Unblock an engineer whose deductible accumulator overshoots

easy
mentoringlost updateisolation levelsreproduction

An engineer on your team has a benefit-application path that occasionally leaves a member's accumulated deductible above the plan limit — a handful of times a day, never in tests. Their code SELECTs the remaining deductible, computes patient responsibility in application code, then UPDATEs the accumulator to an absolute value. They have spent three days adding logging and are now asking whether the database is losing writes. Describe unblocking someone in this position: what you did first, what you let them find themselves, and what you left them able to diagnose next time.

Approach
  1. The probe is whether you teach a method or hand over a patch. Reproduce before explaining: two sessions, both BEGIN, both SELECT the accumulator row, both compute, both UPDATE to an absolute value, both COMMIT. The second overwrites the first and neither errors. Fifteen minutes of that ends three days of logging and, more importantly, ends the theory that the database is at fault.
  2. Be precise about where the anomaly is and is not, because the imprecise version teaches the wrong lesson. A single UPDATE that does its arithmetic in SQL is safe under READ COMMITTED: the blocked statement re-reads the row after the first commits. The loss comes from computing the new value in application code across the statement boundary, so the UPDATE writes an absolute amount derived from a stale read.
  3. Lay out three fixes with their costs rather than naming one. SELECT ... FOR UPDATE serialises writers per member and holds the lock for the transaction, so nothing in that transaction may make an external call. SERIALIZABLE with a retry loop on SQLSTATE 40001 requires the operation to be safely retryable. Per-member partitioning removes contention entirely and adds routing and rebalancing.
  4. Let them pick and defend one. The outcome you want is that they can name the anomaly and the isolation level unprompted next time, not that this particular ticket closes today.
  5. Leave an artefact behind: the two-session reproduction checked in, so the next person meets the interleaving rather than the symptom, and the reasoning is not stored only in your head.
Follow-up
  • A claim is reversed and the accumulator must be credited back. What amount, given the plan design may have changed since?
  • They choose SERIALIZABLE. How do they make the retry safe when the transaction also wrote a claim adjudication row?
  • How would you have noticed this before a member saw it, and what would that check cost per adjudication?
  • 01

    How do you handle error management in your applications?

  • 02

    How do you handle feedback and criticism on your work?

  • 03

    An engineer on your team has a benefit-application path that occasionally leaves a member's accumulated deductible above the plan limit — a handful of times a day, never in tests. Their code SELECTs the remaining deductible, computes patient responsibility in application code, then UPDATEs the accumulator to an absolute value. They have spent three days adding logging and are now asking whether the database is losing writes. Describe unblocking someone in this position: what you did first, what you let them find themselves, and what you left them able to diagnose next time.

PracHub interview preparation framework ↗
Is this an official Garner health interview guide?

No. It is PracHub's own research and practice material for the Software Engineer role at Garner health. Rounds and questions reflect what candidates have reported, not a process Garner health has published, and they change over time. Confirm the current format and scope with your recruiter.

PracHub interview research ↗
What is the typical difficulty level of the interviews?

The interviews at Garner Health are generally considered average to difficult, depending on the specific role and team. Candidates typically find a mix of technical challenges and behavioral questions that require thoughtful responses.

PracHub interview research ↗
How much preparation time is typical?

Most candidates recommend dedicating several weeks to prepare thoroughly. Focus on brushing up on coding skills, understanding system design, and reflecting on past experiences that demonstrate your fit for the role.

PracHub interview research ↗
What differentiates successful candidates?

Successful candidates often demonstrate a strong alignment with the company's mission, showcase their technical expertise clearly, and exhibit effective communication and collaboration skills throughout the interview process.

PracHub interview research ↗
Can you describe the culture at Garner Health?

The culture at Garner Health is mission-driven, emphasizing urgency, accountability, and a commitment to feedback. Employees are encouraged to take initiative, collaborate across teams, and strive for excellence in their work.

PracHub interview research ↗
Sources & methodology 3 sources ↗

Official role evidence, timestamped platform data and clearly labeled preparation advice.