Terrestrial Energy · Software Engineer
Updated · 2026-09-24

Terrestrial Energy Software Engineer
Interview Questions & Guide 2026

THE 60-SECOND BRIEF

A Software Engineer at Terrestrial Energy sits at the critical intersection of advanced computational modeling and nuclear engineering innovation. Unlike traditional software roles that focus on web or mobile applications, this position is dedicated to building, refining, and validating high-fidelity models that simulate complex physical phenomena. Your work directly supports the deployment of Integral Molten Salt Reactor (IMSR) technology, making your contributions vital to the safety, efficiency, and viability of next-generation nuclear power.

Ask how many rounds there are and what each one is before you plan your weeks, because the composition is what your hours should follow. A loop with three coding rounds and one short design conversation deserves a different split from the reverse, and whoever schedules it will usually say plainly which it is.

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

Make every external side effect safe to retryReserve inventory without overselling under concurrent allocationReconcile a movement ledger against derived positions

44 min read

Browse Software Engineer questions

See the practice prompts

13Practice promptsAcross five skill areas
3With worked solutionsIncluded in the practice prompts

A Software Engineer at Terrestrial Energy sits at the critical intersection of advanced computational modeling and nuclear engineering innovation. Unlike traditional software roles that focus on web or mobile applications, this position is dedicated to building, refining, and validating high-fidelity models that simulate complex physical phenomena. Your work directly supports the deployment of Integral Molten Salt Reactor (IMSR) technology, making your contributions vital to the safety, efficiency, and viability of next-generation nuclear power.

In this role, you will transition between software development and deep-domain engineering analysis. You will be expected to translate complex physical requirements—such as heat transfer, fluid dynamics, and nuclear safety parameters—into robust, scalable code. This is a challenging environment where precision is non-negotiable; your software architecture must be as reliable as the physical hardware it models. You will work closely with multidisciplinary teams, bridging the gap between theoretical physics and practical engineering implementation.

Candidates should prepare for a highly technical environment where software development is a tool for scientific discovery. Expect to demonstrate not just coding proficiency, but a deep understanding of the underlying physics relevant to molten salt reactor technology.

01

Recruiter Screen

reported

The title covers product work, platform work, infrastructure, mobile and frontend, and those are different jobs with different loops behind them. A screening call is the cheapest place to find out which one the seat is, and asking reads as experienced rather than fussy. The questions that separate them: what the team is on call for, what the last three projects were, and whether any round happens inside an existing repository instead of a blank file. Then say which of that you have done and which you have not. Claiming the whole posting is the fastest way to be found out one round later.

What to demonstrate

  • Whether you can locate your experience inside one flavour of the role honestly instead of claiming the entire requirements list
  • Whether you name what you have not done, which an experienced screener reads as a level signal and can plan the loop around
  • Whether what you want next matches what the seat is: someone who wants greenfield work landing on a team that mostly operates an existing system is a hire that leaves within the year

How to prepare

  • Mark every line of the posting as done, adjacent or new, and write one sentence for each adjacent line naming the closest thing you actually built
  • Split your last two years into rough percentages across feature work, operating and debugging live systems, and design or review, so a question about scope gets numbers rather than adjectives
  • Bring three questions that discriminate between seats: what the team is paged for, how much of the work is changing existing code versus standing up something new, and what shipped in the last quarter
PracHub interview research ↗
02

Preliminary Technical Assessment

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

Panel Interview

reported

Nobody in the room with you decides this. Interviewers typically write their rounds up separately, often before seeing anyone else's, and the outcome is settled later from those write-ups. A split panel gets resolved by whichever note carries specific evidence, so what you want out of each room is one concrete thing that person could write down: a bug you caught yourself, a trade-off you named, a decision you owned. The rest is arithmetic. The project you describe in a behavioural conversation is often the same system you sketched an hour earlier, and the two accounts have to agree.

What to demonstrate

  • Whether the scale, team size and timeline you attach to a project hold steady when that project resurfaces in a different round
  • Whether each interviewer leaves with a specific thing to cite rather than a general impression of competence
  • Whether a trade-off you defended in one round survives a challenge in another, instead of being quietly swapped for the answer the new interviewer seemed to want
  • Whether a question you have already answered earlier in the day gets the same answer at the same depth, without visible impatience

How to prepare

  • Write a one-page sheet per project fixing the figures you will quote — request volume, data size, team size, elapsed time, what broke — and say them aloud from the sheet until they come out identical every time
  • For each round on the schedule, decide in advance the one sentence you want in that person's notes, then check in a mock that you said it outright instead of leaving it to be inferred
  • Have someone ask you the same project question twice, an hour apart, and diff the two answers for numbers that moved or a trade-off that reversed
PracHub interview research ↗

PracHub editorial advice for the preparation topics above.

01

Holding a database transaction open across a physical or third-party operation

It is natural to open a transaction, lock the position, call the rating or tendering API, and commit on the response, and it works perfectly until the partner's p99 goes from 200 milliseconds to thirty seconds. At that point every request holding a lock on a hot item-node pair queues behind it, the connection pool fills with transactions that are waiting on the network rather than on the database, and an unrelated service sharing the pool fails at the same moment. In this domain the effect is amplified because demand concentrates on a few hot rows during a promotion or a seasonal peak, exactly when partner latency is also degraded. The structural fix is to keep transactions short and local - commit the state change together with an outbox row, let a relay perform the external call, and reconcile asynchronously - accepting at-least-once delivery and making the effect idempotent rather than trying to stretch a transaction over something the database cannot roll back.

02

Treating the database as the truth about physical stock

Every system in this domain drifts from reality, through miscounts, unrecorded damage, mis-scans and theft, so a design with no adjustment path forces the drift somewhere worse. The usual shape is a CHECK constraint or application guard that clamps a position at zero: the negative that would have exposed a missing receipt is silently swallowed, and the first visible symptom is a picker standing at an empty location for an order the system promised. The correct posture is to let the invariant fail loudly - record the cycle-count adjustment as a first-class movement with its own reason code, surface the variance, and let planning see that this location is unreliable - because an adjustment that is indistinguishable from a normal pick destroys the only signal that would have found the root cause. A related version of the same mistake is deleting or updating a movement row to fix a mistake, which leaves the position correct and the history a lie.

03

Issuing one query per row of a result set

Fetch related rows in a single batched query keyed by the ids you already hold, or join them into the original query. A per-row round trip multiplies network latency by the row count, and it looks perfectly fine against the ten rows in your development database.

04

Trusting input because it came from your own front end

Anything crossing a trust boundary is hostile: parameterise queries instead of building SQL by concatenation, validate against an allow-list rather than a deny-list, and bound the size of anything you allocate from a request. Raising this unprompted in an API or design question is a cheap and unusually strong signal.

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

10 technical prompts3 include a worked solution

Re-sum a movement ledger and report positions that disagree

easyWorked solution
aggregationreconciliationappend-only ledger

inventory_movement holds 3 billion immutable rows: item_id, node_id, lot_id (NULL when the item is lot-untracked), state, delta_qty (signed int64 in the item's smallest transacting unit), uom_code, and reversed_by. inventory_position holds one row per (item_id, node_id, lot_id, state) with qty, version and last_movement_id, using lot_id = 0 as the sentinel for untracked. Up to 80 million distinct keys. Report every key whose summed movements disagree with the stored qty, while writes continue. State your time and space bounds and how you bound the comparison.

Approach
  1. Fix the key first. inventory_movement.lot_id is NULL for untracked items and inventory_position.lot_id is 0, so the group key is (item_id, node_id, COALESCE(lot_id, 0), state). Getting this wrong does not error — it silently produces two groups that each look like a variance, and the report becomes noise nobody reads.
  2. Cut the ledger at a watermark, but do not take W = max(movement_id) at the start of the scan. A sequence hands out an id before the inserting transaction commits, so at the instant you read that maximum there are ids below it still in flight and invisible to your snapshot. Summing movement_id <= W misses them on this run, and advancing reconciled_through_movement_id to W makes the miss permanent: every later incremental run starts above those ids and they are never summed again. That is a hole in the ledger's own re-derivation, not a transient skew.
  3. Take a watermark that is provably settled instead. Bound write transactions with a statement or transaction timeout so 'the longest write' is a number T, record (observed_at, max_movement_id) samples periodically, and use as W_safe the largest sampled id whose observed_at is older than T — every id at or below it has committed or rolled back. Sum rows with movement_id <= W_safe, compare only against position rows with last_movement_id <= W_safe, treat anything newer as a write that raced you rather than a variance, and advance reconciled_through_movement_id only to W_safe so the next run starts there instead of re-reading 3 billion rows. If the ledger carries a commit timestamp, cutting on that is the same guarantee without the sampling table.
  4. Sum delta_qty as int64, and include reversal rows. reversed_by is a back-pointer for audit, not an exclusion filter: an original of +10 and its reversal of -10 must both be summed to reach 0. Excluding the original while keeping the reversal produces -10 and a false variance on every corrected key.
  5. Guard the denomination rather than trusting it. uom_code is stored per row because pack factors change over time, so reject — do not sum — any row whose uom_code is not the item's smallest transacting unit, and report those keys separately. A mixed-denomination sum is arithmetically meaningless and looks exactly like a real variance.
  6. Hash aggregation is O(n) time and O(distinct keys) space: 80 million entries at roughly 40-56 bytes each in a typical runtime is 3-5 GB, so quote the number. The bounded-memory alternative is an external sort-merge on the group key — O(n log n) comparisons, resident memory bounded by the merge fan-in rather than by key count, and it streams — or hash-partition by hash(item_id) % P and run P independent passes for 1/P of the peak.
Worked solution 25 min
  1. Write the group key expression down explicitly, including the NULL-to-0 mapping, before any aggregation code.
  2. Derive W_safe from a sample older than the transaction-duration bound T, and state in one sentence which rows on each side are in scope — this is the part that makes the report trustworthy under live writes, and the part that decides whether an in-flight row is re-read next run or lost forever.
  3. Implement the hash aggregation with an int64 accumulator and a uom_code assertion per row, emitting rejected rows to a second output rather than into the sum.
  4. Build a fixture with four keys: one clean, one with a movement missing from the position, one with an original plus its reversal, one with a row in cases where the item transacts in eaches.
  5. Run it, then re-run with the fixture rows shuffled, with a concurrent writer appending past the watermark, and with a writer that reserved an id below max(movement_id) and commits only after the scan finishes — then run the incremental pass and confirm that row is summed there.
EXPECTED RESULTExactly one key is reported as a quantity variance; the reversed key reconciles to zero difference; the mixed-unit key appears in the rejected-rows output rather than the variance list; rows appended past the watermark change nothing; the row whose id was reserved below `max(movement_id)` but committed late still sits above `W_safe`, so the next incremental run sums it instead of skipping it permanently.
Follow-up
  • A key shows a variance of exactly one pick, repeatedly, at one node. What do you look at first, and what would distinguish a duplicate movement from a missed one?
  • The job takes six hours and the variance report is stale by the time anyone reads it. How would you make it incremental without losing the guarantee that it re-derives from the ledger?
  • Who writes the correcting movement, and what reason code does it carry?

Fold late out-of-order status events into a monotonic leg state

hard
semilattice foldevent timeidempotency

Leg statuses form the chain planned < tendered < accepted < picked_up < in_transit < delivered, with exception and cancelled outside it. You ingest 300 million observation_event rows a day carrying subject_id (leg), observed_status, occurred_at, received_at, clock_offset_ms and dedupe_key; five percent arrive over an hour late and a tail arrives a day late. Write the merge that produces each leg's current status, prove the result depends only on the set of events and not their order, and state the per-event cost.

Approach
  1. Define the per-leg state as a tuple ordered totally: (status_rank, occurred_at, source_priority, event_id), and define merge(a, b) as the maximum under that order. Rank leads; occurred_at and the rest only break ties. The proof is then one line: maximum over a total order is idempotent, commutative and associative, so the state space with merge is a join-semilattice and the fold over any permutation of the event set yields the same value. That is also why no global sort is needed — the O(n log n) disappears and the fold is O(1) per event, O(distinct legs) state.
  2. Rank before time, deliberately. occurred_at is device-reported and sometimes wrong; if it led the comparison, the leg's status would be handed to whichever device has the most badly skewed clock. Ranking first means a bad clock can only reorder events within one rank. Correct occurred_at by clock_offset_ms where it was measured, and where the offset exceeds a threshold keep the observation but bar it from winning a tie — the event is still evidence, it just stops being authority.
  3. Make the lattice total, because exception and cancelled are not points on the chain and max over a partial order is undefined for incomparable pairs. Model cancelled as an absorbing top element, and carry exception as a boolean flag merged by OR alongside the rank rather than as a rank of its own — otherwise an exception at rank 4 either blocks delivered at rank 6 or is erased by it, and both are wrong. A product of semilattices is a semilattice, so the order-insensitivity argument still holds over the tuple.
  4. Deduplicate on dedupe_key even though the merge is idempotent. Idempotence covers a byte-identical redelivery; a partner replaying a window with a fresh event_id is not identical, and the tie-break would let the replay displace the original for no reason. Dedupe on the stable key, then fold — both halves are needed, and conflating them is the usual defect.
  5. Partition the stream by subject_id so all events for one leg land on one partition: broker ordering guarantees are per-partition only, and while a commutative merge makes interleaving harmless, a concurrent read-modify-write on the same leg row is still a lost update. Where that cannot be guaranteed, make the write a compare-and-set on a version column, or a conditional update that only raises the state. Because the state is mergeable, shard maps combine cleanly and replaying the whole day converges to the same answer — which is what makes reprocessing safe.
  6. Say where genuine regressions live: a delivered shipment that is refused and returns is a new leg, not a rank decrease. Monotonicity is only sound if real reversals are modelled as new objects, and keeping every superseded observation is what lets you answer the dispute later, so the fold discards nothing — it marks losers superseded.
Follow-up
  • An event arrives a week late for a leg already closed and invoiced. Does it change the status, the on-time metric, both, or neither?
  • Two events for one leg have the same rank and the same occurred_at from different sources. What decides, and is that decision stable across a replay?
  • How would you test order-insensitivity rather than assert it?

Size dock doors from overlapping appointment windows

medium
sweep lineintervalstime zones

For one node and one local calendar day you have up to 200,000 planned dock appointments from shipment_leg: leg_id, planned_arrive_at, planned_depart_at (both TIMESTAMPTZ), plus the node's IANA zone. A door is occupied over [arrive, depart). Return the minimum number of doors that lets every appointment start on time, and the maximal interval over which that peak is sustained. Target O(n log n). Say how you derive the day's boundaries and how you break ties between an arrival and a departure at the same instant.

Approach
  1. Name the result before computing it: with one interchangeable door type, the minimum door count equals the maximum number of simultaneously occupied intervals. That equality is not a heuristic — interval graphs are perfect, so their chromatic number equals their clique number, and greedy left-to-right assignment achieves it. Say this, because it is what licenses solving a scheduling question with a counter.
  2. Emit 2n endpoints, (t, +1) at each arrival and (t, -1) at each departure, sort by t, and at equal t order the -1 before the +1. Half-open occupancy means a trailer leaving at 10:00 frees the door for one arriving at 10:00; ordering arrivals first inflates the answer by exactly the number of back-to-back handoffs, which on a well-packed schedule is most of them.
  3. Scan once with a running counter, tracking the maximum and the endpoint index where it was first reached. The peak is attained on a half-open interval between two consecutive endpoints, [t_i, t_{i+1}), not at an instant — report it that way or the operations team cannot act on it. Extend the interval while the counter stays at the maximum.
  4. Complexity: O(n log n) dominated by the sort, O(n) space. If rows already arrive ordered by planned_arrive_at from an index, use the min-heap variant instead — push each departure, pop all departures at or before the current arrival, and the heap size is the current occupancy. Same time bound, O(peak) space rather than O(n), which matters when peak occupancy is 40 and n is 200,000.
  5. Derive the day's boundaries from the zone, not from arithmetic on instants. Convert local midnight and the next local midnight to instants in the node's IANA zone; on a transition day that span is 23 or 25 hours, so adding 86,400 seconds silently drops or duplicates an hour of appointments. Decide explicitly what a zero-length dwell means — under half-open semantics it occupies nothing — and state it.
Follow-up
  • Doors are typed: some break pallets, some are parcel-only. Does max-overlap still equal the door count?
  • Appointments have a tolerance — a trailer may start up to 20 minutes late without penalty. How does that change the objective, and is it still solvable by a sweep?
  • Half the appointments are actuals and half are plans. Which do you sweep for tomorrow's staffing, and which for last week's utilisation report?

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 ↗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 ↗Worked solution ↗

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

A migration is a cost you chose to pay, not an achievement. The story is what the old system made expensive, what you measured before committing, what kept serving traffic during the cutover, and what you would have done if the numbers had come back flat. Without those, a rewrite reads as taste.

How do you handle uncertainty in input parameters when building a pred…

medium
behavioural and engineering judgement

How do you handle uncertainty in input parameters when building a predictive model?

Approach
  1. Pick a story where you made the decision, not one where you watched it.
  2. Close with what you would do differently, concretely.
  3. Give the blast radius: what could have broken, and what you measured.
Follow-up
  • What would you do differently if you ran that again?
  • How did you know your change caused the improvement?

Unblock an engineer whose replayed feed double-counts

easy
mentoringevent timededuplicationidempotency

An engineer on your team consumes a partner status feed that resends a rolling 24-hour window every night. Their consumer folds each message into shipment_leg.status by last write wins, keyed by partner message id. Deliveries are flipping back to in transit each morning, and some receipts are counted twice. They have been on it two days and ask for help. Describe a time you unblocked someone: how you diagnosed it together, which of the two independent defects you pointed at, what you deliberately let them find, and how you checked the fix held.

Approach
  1. Start by getting the two defects named separately, because they are independent and fixing one hides the other: duplicates are a deduplication problem, and statuses regressing is an ordering problem. A consumer can be perfectly deduplicated and still flap.
  2. Fix the dedupe key first, since it is the cheaper of the two and it is wrong for a stated reason: a partner message id changes on resend, so it identifies the transmission rather than the observation. The stable key is the source plus the device or partner event sequence, enforced as a unique constraint on observation_event.dedupe_key so a replayed window becomes a no-op at the database rather than a judgement in application code.
  3. Then fix the fold: order by occurred_at, compare status_rank on the progress lattice and take the maximum, so a picked_up event arriving after delivered is recorded but cannot lower the leg. Keep the superseded observation rather than dropping it, because it is usually the row that explains a later dispute.
  4. Teach the diagnostic rather than the answer. Ask them to replay one leg's observations in shuffled order and assert the same final status, which is the property that makes the fold order-insensitive and is a test they can write in twenty minutes. Let them discover from their own data that occurred_at is sometimes wrong, which is why clock_offset_ms is stored beside it and why the lattice, not the timestamp, is what guarantees monotonicity.
  5. Check it held with data rather than with a green build: count legs whose status_rank decreased in the last day, expect zero, and keep it as a standing assertion. Say what you did with the rows the old consumer had already corrupted, because leaving them is a decision too.
  6. Be honest about the handoff. Say how long you spent, what you did not do for them, and whether they could explain the fix back to you afterwards, which is the only durable test that the unblocking worked.
Follow-up
  • They ask whether broker ordering guarantees solve this for them. What do you say?
  • An event arrives a week late and its status is below the current one. What should the pipeline do with it, and what should it tell anyone reading the leg?
  • How do you repair the legs already corrupted, given the observations are all still stored?

Ship freight cost attribution with named, dated debt

medium
technical debtcost attributionscope cutting

You have eight working days to get freight cost onto order margin. The correct model allocates each shipment_leg cost across the orders on that leg through a bridge, with a stated basis such as billable weight, and attaches accessorials to the stop that incurred them. The version that fits the window copies linehaul_cost_cents onto each line, split evenly. Describe a time you shipped known debt: exactly what you cut, the guard that made the shortcut visible rather than silent, who agreed to carry it, the date it was paid down, and what it cost while it stood.

Approach
  1. State the accepted failure concretely rather than as a category: on a multi-stop load, an even split charges a two-pallet order the same freight as a twenty-pallet order, and a detention accessorial incurred waiting at one stop is smeared across orders that were not there. Margin on small orders is understated and on large ones overstated, in a way that is systematic rather than random, so it will not average out.
  2. Make the shortcut loud. Emit a counter on legs with more than one order, tag every attributed row with the basis used so downstream consumers can filter, and report the share of cost that went through the even split. A shortcut that reports its own frequency converts an unknown into a number you can take to the paydown conversation.
  3. Bound the exposure before shipping: count historical legs with more than one order and the weight dispersion within them. If ninety percent of legs are single-order parcel, the debt is small and quantified; if a third are multi-stop truckload, the plan does not survive contact and you learned that in a query rather than in a finance review.
  4. Handle the arithmetic correctly even in the shortcut, because integer minor units do not divide evenly. Splitting 100,003 cents across three orders must assign the residue deterministically, to the largest order or to the first by id, and the sum of the parts must be asserted equal to the leg cost; rounding each share independently is how a reconciliation against the carrier invoice ends up off by cents nobody can explain.
  5. Name the owner, the date and where both are written, then report what actually happened: how much cost flowed through the shortcut, whether the paydown date held, and if it slipped, by how long and what finally forced it.
  6. Distinguish debt you can pay from debt that compounds. An attribution you can recompute from shipment_leg and the bridge later is recoverable; one that overwrites the leg cost or discards the stop detail destroys the inputs and cannot be unwound, and a strong answer says it would have refused that version at any deadline.
Follow-up
  • The paydown date arrives and the team has a new deadline. What is different about this conversation from the first one?
  • Finance has been reporting margin off this for six weeks. What do you tell them, and what do you restate?
  • Which version of this shortcut would you have refused outright, and why that one?
  • 01

    How do you handle uncertainty in input parameters when building a predictive model?

  • 02

    An engineer on your team consumes a partner status feed that resends a rolling 24-hour window every night. Their consumer folds each message into shipment_leg.status by last write wins, keyed by partner message id. Deliveries are flipping back to in transit each morning, and some receipts are counted twice. They have been on it two days and ask for help. Describe a time you unblocked someone: how you diagnosed it together, which of the two independent defects you pointed at, what you deliberately let them find, and how you checked the fix held.

  • 03

    You have eight working days to get freight cost onto order margin. The correct model allocates each shipment_leg cost across the orders on that leg through a bridge, with a stated basis such as billable weight, and attaches accessorials to the stop that incurred them. The version that fits the window copies linehaul_cost_cents onto each line, split evenly. Describe a time you shipped known debt: exactly what you cut, the guard that made the shortcut visible rather than silent, who agreed to carry it, the date it was paid down, and what it cost while it stood.

PracHub interview preparation framework ↗
Is this an official Terrestrial Energy interview guide?

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

PracHub interview research ↗
How difficult is the interview process?

Candidates often describe the interview as challenging and highly theoretical. Expect to be tested on your fundamental understanding of physics rather than just your coding syntax.

PracHub interview research ↗
What is the typical timeline for the hiring process?

The process can take anywhere from a few weeks to two months. It is important to stay patient and maintain open communication with your recruiter regarding your status.

PracHub interview research ↗
How should I prepare for the technical panel?

Focus on your core engineering fundamentals. Be prepared to explain your past projects in detail, including the physics behind your models and how you validated your results.

PracHub interview research ↗
What is the culture like at Terrestrial Energy?

The environment is highly technical and mission-driven. The team values precision, logical rigor, and a deep commitment to solving complex engineering problems.

PracHub interview research ↗
Sources & methodology 3 sources ↗

No official company page is cited. Rounds and questions come from candidate reports and PracHub editorial material; each source shows the date it was read.