AIDA Cruises · Software Engineer
Updated · 2026-09-24

AIDA Cruises Software Engineer
Interview Questions & Guide 2026

THE 60-SECOND BRIEF

At AIDA Cruises, a Software Engineer occupies a pivotal role in maintaining the technical excellence that powers the world’s leading cruise line. You are not just writing code; you are building and managing the digital infrastructure that ensures seamless operations, guest satisfaction, and robust CRM solutions. Your work affects the daily experience of both AIDA Cruises' crew and its passengers, bridging complex technical systems and high-stakes hospitality.

Prepare in one language you know well enough to debug in rather than the one you think reads best. Under a clock an unfamiliar language costs you standard-library lookups and iteration mechanics, and that time comes out of your thinking budget, not your typing budget.

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

Derive idempotency keys from attempts, never from retriesDecrement per-night inventory with one conditional updateHold money in minor units, rounding per jurisdiction

46 min read

Browse Software Engineer questions

See the practice prompts

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

At AIDA Cruises, a Software Engineer occupies a pivotal role in maintaining the technical excellence that powers the world’s leading cruise line. You are not just writing code; you are building and managing the digital infrastructure that ensures seamless operations, guest satisfaction, and robust CRM solutions. Your work affects the daily experience of both AIDA Cruises' crew and its passengers, bridging complex technical systems and high-stakes hospitality.

This role requires a unique blend of technical precision and operational awareness. Whether you are managing CRM applications or optimizing software reliability, you will be expected to demonstrate a deep understanding of how your solutions serve the business. It is a challenging, fast-paced environment where the ability to remain calm under pressure—often while troubleshooting real-time technical hurdles—is just as critical as your programming proficiency.

01

Initial Screening

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

Interactive Stages

reported

You cannot drill a format you do not know, so put the preparation into material that travels. Three pieces of your own work, each rehearsed until you can take a follow-up you did not anticipate, will carry a conversation or a code walkthrough equally well. Specificity is what separates that from filler. A number needs its definition before it means anything: a p99 is over some window and measured at some hop, and a server-side figure excludes the queueing and network time a client would see. The number you cannot qualify is the one to leave out.

What to demonstrate

  • Whether your examples carry detail only someone who did the work would hold, such as what the binding constraint actually was, which alternative you rejected and why it was worse, and what you measured on each side of the change
  • Whether a number survives one follow-up, meaning you can say what it was measured over and whether it moved because of your change or merely alongside it
  • Whether a failure is described with the specific change that followed it, rather than a lesson stated in general terms
  • Whether your part in a team effort is stated accurately, including what other people did

How to prepare

  • Write a page on each of three projects covering the constraint, the option you rejected, the measurement before and after, and what went wrong. Cut any line you cannot take a follow-up on, since you are writing the parts you will be pressed on rather than a summary.
  • Recover the real figures while you still have access: request volume, data size, latency with its percentile and window, team size, timeline. Note where each came from, whether a dashboard, a design document or memory, and mark the estimates so you can say which they are out loud.
  • Take your weakest project story to someone who works in a different area and have them ask why four times in succession. The point where you run out of answer is the part to go and re-read before the round.
PracHub interview research ↗
03

Assessment Center

reported

Most of the time lost in this format is not lost to thinking. It goes to a standard-library call you half-remember, an off-by-one in a loop bound, and a debugging loop that mutates code at random until something passes. When output is wrong, stop re-reading the whole function: take the smallest input that reproduces it and walk the state through by hand, printing intermediates if the environment allows. Guessing at a fix without a failing case you understand is how a five-minute bug becomes twenty, and the clock does not pause while you do it.

What to demonstrate

  • Whether you reach the right structure without a detour, and can write it from memory rather than only recall that one exists
  • Whether overflow is considered where the language has fixed-width integers, since a signed 32-bit value stops at 2,147,483,647 and then wraps in Java, is undefined behaviour in C++, and does not arise in Python, whose integers grow instead
  • Whether recursion depth is treated as a constraint on large inputs, given that CPython's default limit is 1000 frames and a deep recursion can exhaust the stack in any language where an iterative version would not
  • Whether a failing case is isolated and explained before any edit is made to the code

How to prepare

  • From an empty file and with no references open, implement the pieces you lean on most: a heap push and pop, an iterative DFS with an explicit stack, and a binary search whose midpoint is written lo + (hi - lo) / 2, which avoids the overflow that (lo + hi) / 2 can hit in a fixed-width integer type
  • Time yourself on the ten library calls you look up most, such as sorting with a custom comparator, splitting and joining strings, and finding the next key at or above a value in an ordered map, until the lookup is gone
  • Take a solution you know is broken and, before touching it, write one sentence naming the input, the expected value and the actual value. Repeat until you do it without deciding to.
PracHub interview research ↗
04

Final Technical Evaluation

reported

Coding rounds mostly set a floor. They decide whether you clear the bar, not where you land on the ladder. Level tends to come out of the design discussion and the ownership stories, so the question worth auditing beforehand is whether the scope you describe matches the scope of the job. Work that stops at your own service, or a story whose hard part was writing the code rather than getting several people to agree on an interface, reads a level below where you think you are interviewing, and that gap is usually resolved downwards.

What to demonstrate

  • Whether the largest thing you describe owning ran end to end — the decision, the migration path, the rollout, and what you did when it went wrong — or stopped at the change you merged
  • Whether design answers include what you would not build, what you would defer, and what you would measure before committing, rather than only what the boxes are
  • Whether a disagreement in a story was settled with something checkable — a benchmark, a prototype, a written proposal — instead of by seniority or by waiting it out
  • Whether you can say which calls you made alone and which you escalated, and why the line sat where it did

How to prepare

  • Write your largest piece of owned work as a timeline of decisions — who decided what, when, and what you did when the plan broke — then delete every sentence whose subject is "we" and see how much survives
  • Take one system you know well and drill the migration answer: how old and new paths run side by side under live traffic, how you compare their outputs, what the rollback is once writes are going to both, and which step you would not automate
  • Map each line of the ladder in the job posting to a specific thing you have done, find the line you cannot support, and prepare the closest evidence you have plus an honest account of the gap
PracHub interview research ↗

PracHub editorial advice for the preparation topics above.

01

Representing money as a float, and rounding once at the end

Currencies have different ISO 4217 exponents, so the reflex of multiplying by 100 is wrong for zero-decimal currencies such as JPY and KRW and for three-decimal currencies such as KWD, BHD and OMR, and it is wrong in a direction that gets worse with volume. Separately, taxes and fees are levied per night and per jurisdiction and each rounds on its own, so the sum of rounded per-night amounts is not the rounded sum, and the two differ by a few minor units on a typical stay. Neither bug shows up in testing with a single currency and a single-night booking. Both show up as a reconciliation mismatch against the supplier or the processor, at which point somebody has to explain a line-item discrepancy across thousands of bookings without a per-night audit trail to do it with.

02

Implementing an amendment as a cancel followed by a rebook

On a sold-out date, releasing the old nights first hands the unit to a concurrent booker and the traveller's own amendment fails, leaving them with nothing; taking the new nights first double-counts the overlap and can fail the capacity check against the traveller's own existing booking. Either ordering also detaches the amendment from its policy snapshot, so the rebooked stay silently acquires today's cancellation terms and today's price rather than the ones that were agreed. Compute the amendment as a per-night delta inside one transaction: release only the nights being dropped, take only the nights being added, leave the overlap untouched, and derive the payment adjustment from the stored policy snapshot rather than from current prices.

03

Sharing mutable state with no stated owner

Say which thread, request or task owns each mutable structure, and what protects it when the answer is more than one: a lock, a queue that hands ownership across, or an immutable copy per reader. A structure documented as safe for concurrent reads is usually not safe for a concurrent write alongside those reads.

04

Retrying a write that is not safe to repeat

A timeout tells you nothing about whether the server applied the write, so a blind retry of a create or a charge can duplicate it. Either make the operation idempotent, with a caller-supplied key the server deduplicates on or a conditional update, or do not retry it; and use exponential backoff with jitter so the retries of many clients do not synchronise into a second outage.

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

11 technical prompts3 include a worked solution

Order saga compensations over the steps that actually ran

medium
topological sortdagsagacompensation

A booking saga is a DAG of at most 64 steps with at most 200 edges. Each step carries a compensating action, and some are flagged not cleanly compensable. At runtime every step is in one of three states: completed, unknown (the call timed out and the outcome is not known), or not_started. Produce a valid forward execution order, detect a cycle in the step graph, and on failure produce the order in which compensations must run over the steps that actually ran. Target O(V + E). State explicitly what your compensation order does with an unknown step.

Approach
  1. Kahn's algorithm for the forward order: compute in-degrees, seed a queue with the zero-in-degree steps, pop and decrement. O(V + E), and with V <= 64 an in-degree array plus an adjacency list is the whole data structure.
  2. Cycle detection falls out of the same pass. If fewer than V nodes are emitted, the residual nodes lie on or downstream of a cycle — report that set rather than a boolean, because the residual is the diagnosis.
  3. For compensation, build the induced subgraph over the steps in completed or unknown, topologically sort that subgraph, and reverse it. Reversing the planned order instead is the bug: it schedules compensations for steps that never ran, and a bare remaining_units = remaining_units + n release applied against a hold that was never taken hands out capacity that does not exist.
  4. An unknown step is compensated, never skipped. Its compensation begins with a read-back against the stored idempotency key — resolve what actually happened, then cancel it if it happened. A design that treats a timeout as a failure and skips the compensation is how a supplier reservation survives a booking that failed.
  5. Surface the not-cleanly-compensable step at design time rather than at runtime. A supplier confirm against an API with no read-back cannot be resolved automatically, which forces a manual reconciliation queue and changes the operational scope of the feature, so it is a thing to raise in the first five minutes.
  6. Trade-off: reverse topological order is a partial order, so independent compensations can run concurrently and shorten the window in which money is held without inventory. The price is that a partial compensation failure leaves a mixed state, so each compensation still has to be individually idempotent before you are allowed to parallelise.
Follow-up
  • Two compensations are independent in the DAG but contend on the same unit-date rows. Does the partial order still hold, and what do you add?
  • A compensation itself times out. What state does that step enter, and how does the reconciler tell it apart from one that never started?
  • Where does the transactional outbox sit in this graph, and what does a duplicate delivery do to each consumer?

Price every flexible date range without rescanning nights

hard
prefix sumsincremental windowread amplificationlatency budget

One unit type has cached per-night rows over a 43-day slice, each with price_minor, remaining_units, closed_to_arrival, closed_to_departure, stop_sell and min_los. A flexible search asks for 15 candidate check-in dates crossed with lengths of stay 1 through 28, for a party needing one unit, across 5,000 candidate unit types, inside a 300 ms budget. For each length of stay, return the cheapest legal check-in date and its total price. Rescanning the nights of every range is correct; say why it does not fit the budget, and give an approach that does.

Approach
  1. Quantify the naive cost before optimising it. Rescanning touches the sum of L for L = 1..28, which is 406 nights per check-in, so 15 x 406 = 6,090 night touches per unit type and about 3.0 x 10^7 per request across 5,000 candidates. At the tens of millions of simple operations per second a managed runtime delivers on one core, that is most or all of a 300 ms budget before pricing, ranking and serialisation are paid for.
  2. Exploit the incremental structure: for a fixed check-in, extending the length of stay by one night adds exactly one night to the running price sum, one candidate to the running minimum of remaining_units, and one term to the running OR of stop_sell. Sweep check-in on the outer loop and length of stay on the inner, carrying those three accumulators.
  3. Each (check-in, length) evaluation is then O(1), giving 15 x 28 = 420 night touches per unit type and about 2.1 x 10^6 per request — roughly 14.5x less work — with O(1) extra space and no preprocessing pass at all.
  4. Apply every restriction at its own night, because their scopes genuinely differ. closed_to_arrival, min_los and max_los are read from the arrival row only. stop_sell is the OR over the booked nights [i, i+L). closed_to_departure is read from the row at index i + L, which is the checkout date and is not a booked night; if that row falls past the end of the cached slice, the range is unevaluable and must be excluded rather than assumed open.
  5. If the access pattern were arbitrary (i, j) pairs in random order rather than a structured sweep, the incremental trick dies and the right structures are a prefix sum for price (O(D) build, O(1) query) and a sparse table for the range minimum of remaining_units (O(D log D) build, O(1) query). The sweep beats both here only because the query order is under your control.
  6. The remaining lever is not algorithmic. The 5,000 candidate unit types dominate the cost, so prune before this loop — by destination geometry, by a per-night price floor, or by taking only the top-ranked candidates — and return partial results with an explicit completeness flag when the budget expires anyway.
Follow-up
  • The party needs two units rather than one. What changes in the accumulators, and does any restriction scope change with it?
  • Rank by total price per night instead of total price. Can you prune across lengths of stay now, and what makes that valid?
  • The cached slice is per (unit_type, stay_date). What does one supplier push invalidate, and how do you stop a popular date from stampeding the recompute?

Merge k sorted supplier streams under a deadline budget

mediumWorked solution
heapk-way mergetop-kdeadline budget

A search fan-out returns K <= 40 supplier result streams, each already sorted ascending by total_minor converted to the display currency using the quote's pinned fx_rate_id. Streams are iterators and advancing one costs a network read. Up to 10,000 results exist across all streams; the page needs the k = 30 cheapest. The request carries a remaining deadline budget, and a stream that misses it is dropped. Return the k cheapest offers plus a completeness flag, minimising elements read. State the cost in heap operations and in stream reads, and state what the flag guarantees.

Approach
  1. Seed a min-heap with one head element per stream: K entries, heapified in O(K). Do not materialise all 10,000 results — a full merge is O(N log K) with N = 10^4 to produce 30 answers, which is more than two orders of magnitude of wasted network reads.
  2. Pop the minimum, emit it, then advance only the stream it came from and push its next element. After k pops you hold the k globally cheapest. Elements read is bounded by K + k - 1 = 69, and heap work is k pops at log K, roughly 30 x 5.3 comparisons. Skipping the refill on the final pop is what saves the last read.
  3. Derive each stream read's timeout from the budget remaining at the instant the read is issued, not from a fixed constant. A fixed per-call timeout lets the last connector spend a budget that earlier connectors already consumed, and the request blows through its deadline while every individual call looks compliant.
  4. When a stream is dropped, remove it from the heap and set the completeness flag. State the guarantee precisely: the k-th price returned is an upper bound on the true k-th price, because a dropped stream could only have contributed something cheaper. That is a claim a caller can act on; a bare boolean flag is not.
  5. The sortedness precondition is load-bearing. If the connector contract does not promise it, the shape changes to a bounded max-heap of size k over every result, O(N log k) with no early exit and every element read. Which regime applies is a property of the connector, worth pinning before writing either version.
  6. Comparison must happen on one currency at one pinned FX rate. Comparing price_minor across currencies orders offers by the accident of their ISO 4217 exponent, and re-fetching a live rate mid-merge makes the ordering non-reproducible in a later dispute.
Worked solution 20 min
  1. Take four streams: S1 = [12000, 18000, 25000], S2 = [9000, 21000], S3 = [30000], S4 which times out before returning anything.
  2. Seed the heap from the three responsive heads and record the reads so far.
  3. Pop three times, refilling from the popped element's stream after the first two pops and stopping before refilling on the third.
  4. Tally elements read and the maximum heap size, and check them against the K + k - 1 bound with K = 3 responsive streams and k = 3.
  5. State the completeness flag and the bound it implies against S4's unknown contents.
EXPECTED RESULTResults are 9000, 12000, 18000 in that order. Completeness flag is false. Five elements are read (three heads plus 21000 and 18000); 25000 is never read. Heap size never exceeds 3, matching K + k - 1 = 5 reads.
Follow-up
  • One stream is consistently slowest but also consistently cheapest. How do you stop the deadline from systematically excluding the best prices?
  • Results within a stream are sorted by price, but the page ranks by a blended score. What breaks, and what do you need from the connector to fix it?
  • Model the tail: if each of 20 connectors independently exceeds its own p99 one percent of the time, what fraction of requests loses at least one, and does that number justify partial results?

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 ↗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.

Reverse your own decision on hold expiry

medium
reversibilityhold lifecyclemeasurementjudgement

You chose sweeper-only expiry for inventory_hold_night: a job flips lapsed rows from active to expired and returns units, and the availability read trusts the state column alone. Production contradicted the choice. Describe a decision you reversed: what you originally believed and why it was reasonable, the specific observation that changed your mind, how long you waited before reversing, what the reversal cost, and what you kept from the first design. Name the metric that moved and one that did not.

Approach
  1. Say what made the original choice reasonable, because a reversal story with no defensible starting point reads as carelessness rather than judgement. Sweeper-only expiry keeps the read path trivial and lets the partial index on (unit_type_id, stay_date) WHERE state = 'active' answer availability with no clock in the query.
  2. Give the observation that broke it, in numbers. When the sweeper falls behind by L seconds, up to L seconds of lapsed holds still read as active, so availability is understated and searches lose bookable inventory on exactly the dates where holds are densest, which are the dates that sell. Availability understatement is the metric that moves first.
  3. Separate it from the opposite failure so the reversal is not mistaken for a panic. A sweeper that releases a hold the booking path has already converted increments capacity back and oversells, which is why the release must be conditional on the active-to-released transition and not a bare increment. Both failures are real and they pull in different directions.
  4. State the reversal concretely: evaluate expiry against the clock at read and write time, filtering active rows by expires_at_utc, and keep the sweeper for cleanup. Name the constraint that shapes it, namely that now() is not immutable so 'not expired' cannot be a partial index predicate; expiry is always a filter over the active set, which is exactly why the sweeper has to keep that set small.
  5. Report the cost honestly: a re-tested read path, a migration of in-flight holds, and a measurable increase in rows filtered per availability query. Then name a metric that did not move, because a candidate who claims everything improved is not measuring.
  6. Close on how long you waited and what you would watch earlier next time. Waiting for two data points beats reversing on one anecdote; waiting for a quarter is a different mistake.
Follow-up
  • What is the right TTL for a hold, and what evidence would set it rather than a round number?
  • How do you stop the sweeper from releasing a hold that the booking path converted a millisecond earlier?
  • If the sweeper falls two minutes behind under load, what does a traveller see and what does the revenue graph show?

Estimate an ingest rewrite you have never attempted

hard
estimationingestwrite amplificationmigration

You are asked to estimate replacing the ARI ingest path so that a supplier's full refresh applies as a set difference over the affected horizon instead of row-by-row upserts, across roughly 450 forward days for hundreds of thousands of unit types, with no degradation of the search read path during the change. You have never built this. Give the estimate, the decomposition behind it, the three unknowns that dominate the range, the spike you would run first, and the condition under which you would come back and re-estimate.

Approach
  1. Decompose into shippable pieces before producing any number: refresh semantics, the write path itself, a dual-run comparison against the current path, per-supplier cutover, and rollback. Estimating the whole as one figure is the failure mode; estimating five pieces gives you somewhere to put the uncertainty.
  2. Name the semantic unknown first, because it dominates and it is not an engineering problem. A set difference needs the affected horizon, and suppliers differ in whether a full refresh declares its horizon or leaves you to infer it. If you must infer, the correct behaviour on an ambiguous message is a decision with a revenue consequence: withdraw too much and you stop selling live dates, withdraw too little and you keep selling dates the supplier has pulled.
  3. Quantify the write-amplification unknown rather than describing it. One refresh rewriting a 450-day horizon for a large supplier is millions of unit-date writes in minutes against the hottest table in the system; in PostgreSQL every update writes a new row version, so this produces dead tuples and vacuum pressure, and HOT updates only avoid index churn when no indexed column changes and the page has room. Whether the read path survives that is measurable, not arguable.
  4. State the third unknown as the one that is usually discovered late: how many suppliers actually send full refreshes, how often, and how large, because the cutover cost is per supplier and the tail of odd behaviour is where the schedule goes.
  5. Design the spike to collapse the widest unknown in the least time: replay the largest historical refresh you have against a copy of the table and measure rows touched, wall clock, dead tuples produced and the search path's p99 during the replay. Two days of that is worth more than a week of design.
  6. Give the estimate as a range with the multiplier attached to a named unknown, plus a re-estimate trigger such as the replay exceeding a stated p99 impact. A single number with no stated dominating unknown is the answer that gets you held to it.
Follow-up
  • Halfway in, the replay shows the read path degrading badly. What do you change, and does the estimate move?
  • How would you run both paths in parallel and prove they agree, without doubling the write load on the hot table?
  • Someone needs a date to give a partner. What do you commit to, and what do you explicitly not commit to?

Resolve a review disagreement over an inventory decrement

easy
code reviewconcurrency controlisolation levels

A colleague's pull request reads remaining_units from ari_daily, checks it against the requested units in application code, then issues UPDATE ari_daily SET remaining_units = remaining_units - :n for each night. You believe it must be one conditional UPDATE per night with an explicit row-count check, and that the isolation level has to be a stated decision. Describe a code review disagreement of this kind that you had: what you wrote in the comment, what evidence ended it, what you conceded, and what the engine actually does under the isolation level the code assumed.

Approach
  1. Write the failing interleaving in the comment rather than a principle. Two requests read remaining_units = 1, both pass the application check, both decrement, and the row lands at -1 or is clamped by the CHECK into a constraint error that surfaces as a 500 rather than as sold out. A four-line trace is harder to wave away than 'this is a race'.
  2. Give the replacement as a statement, not a description: UPDATE ari_daily SET remaining_units = remaining_units - :n WHERE unit_type_id = :u AND stay_date = :d AND remaining_units >= :n, one per night, inside one transaction, with every statement's row count inspected before commit. Say that the CHECK (remaining_units >= 0) is the last line of defence and not the concurrency control.
  3. State the isolation behaviour precisely for the engine in play, because this is where reviews go in circles. Under PostgreSQL READ COMMITTED a blocked UPDATE re-reads the row version that the blocker committed and re-evaluates its WHERE clause, so the guard still holds and a losing writer gets zero rows. Under REPEATABLE READ or SERIALIZABLE the same statement instead raises a serialisation failure, SQLSTATE 40001, which the caller must catch and retry with a bounded budget. Those are different code paths.
  4. Raise the second defect while you are there: the nights must be locked in ascending stay_date order. Two multi-night bookings over overlapping ranges that lock in request order deadlock, and that is a separate bug from the lost update.
  5. End the disagreement with a runnable artefact. A two-connection test that interleaves the two transactions and asserts the second gets zero rows takes twenty minutes and converts the discussion into a red test, which is the only thing that reliably ends this class of argument.
  6. Concede what is genuinely arguable. Retry budget, whether to hold or to take inventory at a different step, and whether the allowance applies here are judgement calls; the read-then-write is not.
Follow-up
  • The author says the window is microseconds and the traffic is low. What is your answer?
  • Where exactly do you put the retry for a 40001, and what is the budget before you give up on the booking?
  • How would you write the test so it fails reliably in CI rather than one run in fifty?
  • 01

    You chose sweeper-only expiry for inventory_hold_night: a job flips lapsed rows from active to expired and returns units, and the availability read trusts the state column alone. Production contradicted the choice. Describe a decision you reversed: what you originally believed and why it was reasonable, the specific observation that changed your mind, how long you waited before reversing, what the reversal cost, and what you kept from the first design. Name the metric that moved and one that did not.

  • 02

    You are asked to estimate replacing the ARI ingest path so that a supplier's full refresh applies as a set difference over the affected horizon instead of row-by-row upserts, across roughly 450 forward days for hundreds of thousands of unit types, with no degradation of the search read path during the change. You have never built this. Give the estimate, the decomposition behind it, the three unknowns that dominate the range, the spike you would run first, and the condition under which you would come back and re-estimate.

  • 03

    A colleague's pull request reads remaining_units from ari_daily, checks it against the requested units in application code, then issues UPDATE ari_daily SET remaining_units = remaining_units - :n for each night. You believe it must be one conditional UPDATE per night with an explicit row-count check, and that the isolation level has to be a stated decision. Describe a code review disagreement of this kind that you had: what you wrote in the comment, what evidence ended it, what you conceded, and what the engine actually does under the isolation level the code assumed.

PracHub interview preparation framework ↗
Is this an official AIDA Cruises interview guide?

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

PracHub interview research ↗
How difficult are the interviews at AIDA Cruises?

Candidates generally report the difficulty as average. The process is thorough but focused on practical, real-world scenarios rather than abstract puzzles.

PracHub interview research ↗
How long does the hiring process usually take?

While it can vary, results are often shared within a few days of your final interview.

PracHub interview research ↗
What is the most important thing to prepare for?

Be prepared to discuss your specific past duties and demonstrate your problem-solving logic. Candidates report that interviewers value explaining your "why" as much as your "how."

PracHub interview research ↗
Will there be a practical assessment?

Depending on the specific team and role, you may be asked to perform a practical task or participate in a group-based problem-solving exercise.

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.