Interview guides · Carnival Cruise Line
Carnival Cruise Line Software Engineer Interview Guide
Carnival Cruise Line Software Engineer interview guide: reported rounds, Apex, ServiceNow, SQL and ship-to-shore sync questions, plus a 7-day plan.
The 60-second brief
Carnival Cruise Line operates a fleet of cruise ships, and candidates describe the software behind it as a hybrid estate: systems that run onshore plus systems that must keep working aboard a ship where connectivity is limited or drops out. The platforms candidates name are ServiceNow, Salesforce and custom-built hospitality applications, with APIs and integration points linking corporate systems to shipboard databases, work on the guest boarding process, and growing interest in AI-related tooling. A Software Engineer in this loop builds and maintains that platform and integration code and debugs production problems in it.
This guide is for Software Engineer candidates, including the mid-level and senior profiles candidates describe. It covers the platform questions (Salesforce Apex and ServiceNow), the algorithm, SQL and testing questions, the integration and offline-sync design questions, the AI question, and the project-based behavioral prompts. Pick the platform track the recruiter says the team uses and spend your extra time there.
Candidates report five stages over roughly 4-6 weeks: a recruiter screen, online assessments, technical interviews, panel interviews and a final offer stage. Some teams reportedly add cognitive testing or a take-home exercise.
Focus areas
- ServiceNow platform development
- ServiceNow application development
- Role-specific prior project evidence
Overview
The reported questions fall into three groups. The first is platform craft: walking through a complex Apex page or class you built, managing Salesforce governor limits in triggers and batch jobs, customizing ServiceNow workflows and integrations, and explaining client-side versus server-side scripting in ServiceNow. The second is general engineering: finding duplicate passenger-booking records, tuning a slow SQL query, testing edge cases under time pressure, and building a queue for real-time guest notifications. The third is integration: a secure REST API between onshore databases and a ship server, offline synchronization, a legacy-to-cloud integration story, and an AI and predictive-maintenance question.
Candidates report that the team's stack varies: some teams are mostly Salesforce and Apex, others mostly ServiceNow, others custom cloud-native work. Candidates are also advised to expect a request to show real work, such as code snippets or architecture diagrams from past Apex or ServiceNow projects. That means your preparation has two halves: rehearsing one project you can explain line by line, and rehearsing the standard questions so the answers come out structured.
The ship setting changes how design questions should be answered. Satellite links are slow and can disappear, so a design that assumes the shore database is always reachable is incomplete. Practise saying where data is written first, how changes are queued and replayed, how duplicate delivery is made harmless, and who wins when both sides edited the same record. The same vocabulary (idempotency keys, outbox, cursors, reconciliation) answers the REST sync question, the offline question and the notification queue question.
The requirements candidates describe list Java, C# or JavaScript, SQL, and REST or SOAP APIs, with AWS or Azure, Docker, Kubernetes and AI-related stacks as extras. Pick one language for the coding questions and stay in it, and be ready to talk about SOAP as well as REST, since legacy integration is a reported theme.
The Carnival Cruise Line interview process
Candidates describe five stages over about 4-6 weeks. The descriptions below say what is reported for each stage and how to prepare for it.
Recruiter Screen
Candidates describe this as a first conversation with a recruiter to judge fit for the role. Use it to learn which platform the team works on, since candidates report that some teams are mostly Salesforce and Apex, some mostly ServiceNow and some custom cloud-native, and to settle logistics such as timeline, location and in-office expectations early.
What to demonstrate
- Fit for the role, which is the reported purpose of the screen
- How your background lines up with the team's platform (Apex, ServiceNow or custom cloud work)
- Practical constraints such as location and timeline
How to prepare
- Write a short summary of your career that names your platform experience and one project you could later show as code or a diagram.
- Prepare questions for the recruiter: which stack the team uses, whether a cognitive test or take-home applies, how many onshore versus onboard systems the team touches, and the in-office expectation.
- Decide in advance how you will describe your years of experience, because candidates describe different expectations for mid-level and senior roles.
Online Assessments
Candidates describe preliminary technical evaluations covering coding skills and logical reasoning. Depending on region and team, some loops reportedly include a cognitive or concentration test, and some add a take-home exercise. Treat the stage as timed, so rehearse under a clock.
What to demonstrate
- Working code and logical reasoning, as the stage is described
- Basic algorithmic skill, such as the duplicate-record problem
- Speed and accuracy on pattern or logic puzzles where a cognitive test is used
How to prepare
- Solve the duplicate passenger-booking problem in your chosen language with a hash map, then with sorting, and state complexity for each.
- Practise timed logic and pattern-recognition puzzles if the recruiter confirms a cognitive test.
- Rehearse short answers to the ServiceNow client-side versus server-side and workflow-customization questions in case platform knowledge is tested early.
Technical Interviews
Candidates describe these interviews as focused on past projects, coding skills and platform expertise. Expect to open code or a diagram from earlier work and defend its design, and to answer Apex and ServiceNow questions such as governor limits, asynchronous processing and scripting scope, alongside algorithm and SQL questions.
What to demonstrate
- Depth in the platform the team uses (Apex or ServiceNow)
- Coding ability and algorithmic reasoning
- How well you explain and defend your past project work
How to prepare
- Prepare a walk-through of one Apex class, trigger or page, or one ServiceNow application, covering data flow, why you chose that design, and what went wrong in production.
- Write a bulk-safe trigger handler and a Batch Apex class from memory, and say which governor limit each pattern protects against.
- Practise the SQL tuning answer on a real query: read the plan, add or fix an index, and re-measure.
Panel Interviews
Candidates describe interviews with several team members covering system architecture and behavioral scenarios, and some report that panelists stayed off camera. Narrate your reasoning aloud and ask clarifying questions so the conversation stays two-way. This is the stage where the ship-to-shore sync and legacy integration topics fit best.
What to demonstrate
- System architecture reasoning, as the stage is described
- Behavioral scenarios drawn from your past work
- How clearly you communicate when you cannot see reactions
How to prepare
- Design the onshore-to-ship guest data sync end to end on paper: authentication, change log, batching, idempotent replay, conflict rule and reconciliation.
- Prepare two integration stories with a clear decision you owned: one legacy system joined to a modern platform, and one outage or sync problem.
- Practise answering to a blank screen: state the plan first, then the details, and pause to check direction.
Final Offer Stage
Candidates describe the last stage as a discussion of the offer after the earlier stages are complete, rather than another assessment. Candidates are advised to have settled location, in-office and timeline questions with the recruiter beforehand.
What to demonstrate
- Agreement on the terms and logistics of the offer
- Any remaining questions about the team and its stack
How to prepare
- List the open items you want written down, such as location, in-office expectations, relocation support and start timeline.
- Have specific questions about the team's platform mix and how onshore and onboard systems are split, based on what you learned earlier in the loop.
Where candidates lose points
Describing an Apex or ServiceNow project vaguely, with no code or diagram
Candidates are advised to bring snippets or architecture diagrams. Pick one project, and prepare the class or script structure, the data flow, one production problem and its fix, so you can go into detail without notes.
Answering the governor limits question with "use batch" and no limits named, or putting SOQL or DML inside a loop
Name the limits (queries, DML statements, CPU time, heap), show bulkified code that gathers Ids, queries once and updates a collection once, and explain when Queueable or Batch Apex is the right move.
Designing the ship sync as if the shore database were always reachable
Start from the outage: local write, durable outbox, idempotency key, acknowledged cursor, conflict rule and reconciliation. Say who owns each field, and how replay after reconnect avoids double-applying changes.
Solving the duplicate-bookings problem with pairwise comparison, or only exact matching
State the O(n squared) baseline, then normalize fields, group by a blocking key or hash, and compare within groups. Say how you would handle near-duplicates, and that the SQL version uses GROUP BY with HAVING COUNT or ROW_NUMBER.
Mixing up where ServiceNow scripts run, for example doing heavy GlideRecord queries in a client script
Be able to say client scripts and UI policies run in the browser, while business rules and script includes run on the server, and that a client reaches server data through GlideAjax or server-side logic.
Questions & practice
Walk me through how you would test your code for edge cases under a tight deadline.
Medium · Logical Reasoning & Algorithmic Problem
Approach
- Rank by risk first: list the code paths where a bug corrupts data, charges money or touches guest records, and test those before anything cosmetic. Say out loud which tests you are deferring so the gap is visible.
- Enumerate edge cases systematically with equivalence partitioning and boundary values: empty input, a single element, null or missing fields, zero, negative and maximum values, off-by-one at range ends, duplicates, unicode names, and time zones or daylight-saving transitions in date logic.
- Write table-driven (parametrised) tests so each new edge case is one row, and add a property-based test (for example with Hypothesis) for an invariant such as 'the output has no duplicate ids' when the input space is large.
- Test the failure paths, not only the happy path: a timeout from a dependency, a partial write, a retry of the same request. On Salesforce, run the trigger with 200 records to prove it is bulk-safe.
- Protect the release itself. Ship behind a feature flag or with a rollback plan, add logging on the risky branch, and open a ticket for the deferred tests with the reason they were skipped.
Follow-up
- Which tests would you skip, and how would you record that risk?
- How do you test logic that depends on the current date or time zone?
- A bug escaped to production anyway; which test would have caught it, and why was it missing?
Explain how you would implement a queue system to handle real-time notifications for guests onboard.
Medium · Logical Reasoning & Algorithmic Problem
Approach
- Split the system into producers (services that emit a notification event), a durable queue or broker (RabbitMQ, Kafka, SQS, or a database table on a small server), consumer workers, and delivery channels such as app push or WebSocket. Running the broker on the local ship server keeps delivery working when the satellite link is down.
- Use the transactional outbox pattern: write the notification row in the same database transaction as the business change, and have a relay publish it. That avoids the dual-write bug where the change commits but the message is lost, or the reverse.
- Plan for at-least-once delivery. Give every notification a unique id, have consumers and the client app drop ids they have already seen, and retry failures with exponential backoff and jitter. After N attempts the message moves to a dead-letter queue for inspection.
- Handle priority and ordering. Use separate queues or a priority queue (a binary heap with O(log n) insert and pop, keyed by priority then timestamp) so safety or time-critical messages are never stuck behind promotional ones. Partition by guest id when one guest's messages must stay in order.
- Add an expiry time to each message so a reminder whose event has passed is dropped, not delivered late. Keep a per-guest inbox so a device that reconnects can fetch what it missed, and rate-limit per guest so a bug cannot flood a phone.
Follow-up
- A guest's phone is offline for several hours; what do they see when it reconnects?
- Can you guarantee a guest never receives the same notification twice? What would that take?
- One consumer slows down and the queue backs up; how do you detect it and what do you shed first?
Collapse a redelivered event batch into per-aggregate high-water marks
Easy · hashing · at-least-once · aggregation
You drain a batch of up to 5,000,000 events, each (aggregate_id BIGINT, aggregate_version INT, event_type, payload). The log guarantees order within one aggregate only; the batch merges 64 partitions, and a relay failover has redelivered a range, so an older version for an aggregate can appear after a newer one. Given a map of last_applied_version per aggregate, produce the events worth applying, at most one per (aggregate_id, version), plus the count discarded. Target O(n) time. State the memory for 2,000,000 distinct aggregates and what you do when it does not fit.
Approach
- One pass, one hash map from aggregate_id to the highest version kept, and a discard counter. An event whose version is at or below last_applied_version for its aggregate is dropped without further work, which is the whole reason the event carries its version rather than a delta. O(n) expected time, O(d) space in distinct aggregates.
- Keep the maximum, never the last occurrence. The redelivered range means the final appearance of an aggregate in the batch can be an older version than one seen earlier in the same batch, so last-wins applies stale state over newer state and the projection regresses with no error anywhere.
- Cost the memory instead of calling it large: an 8-byte key plus a 4-byte version is 12 bytes of payload, and an open-addressed table held at a 0.7 load factor costs roughly 17 bytes per entry before per-slot metadata, so 2,000,000 aggregates is tens of megabytes in a native layout and several times that in a runtime that boxes both key and value.
- If the distinct set exceeds memory, partition on hash(aggregate_id) mod P and reduce each partition independently. Every event for one aggregate hashes to the same partition, so the per-partition result is exact and the merge is concatenation rather than a second reduction.
Follow-up
- The payload is a patch rather than a snapshot, so applying only the highest version loses the intermediate changes. What changes in your reduction?
- How do you detect that version 7 arrived while version 6 was never delivered, and what should the consumer do about the gap?
How do you optimize a slow-running SQL query that is impacting onboard systems?
Medium · Logical Reasoning & Algorithmic Problem
Approach
- Reproduce the problem and read the actual plan before changing anything: EXPLAIN (ANALYZE, BUFFERS) in PostgreSQL, or the actual execution plan in SQL Server. EXPLAIN ANALYZE really runs the statement, so wrap any DML in a transaction and roll it back.
- Compare estimated rows with actual rows at each node. A large gap usually means stale statistics (run ANALYZE or UPDATE STATISTICS) or correlated predicates, and that gap is what leads the planner to pick a nested loop over a huge input or a full scan.
- Look for predicates that cannot use an index: a function wrapped around the column (WHERE DATE(created_at) = ...), a leading wildcard (LIKE '%smith'), an implicit type conversion, or an OR across different columns. Rewrite them as range conditions on the bare column.
- Index deliberately. Build a composite index with equality columns first and the range column last, and add INCLUDE columns for a covering index so the query avoids heap lookups. Every index adds cost to inserts and updates, so check the write path on the same table.
- Check what sits around the query. Is the same query running once per row from the application (N+1)? Is deep OFFSET pagination involved, where keyset pagination would help? Does SELECT * pull wide columns? Is the time spent waiting on locks rather than executing? Measure before and after on realistic data volume, since a small server may hold far less in memory than the onshore one.
Follow-up
- The plan now uses the index but the query is still slow; what do you look at next?
- The new index sped up the read but slowed the nightly load; how do you decide what to keep?
- The same query is fast in one environment and slow in another with similar data; what could differ?
Find version gaps and relay lag with window functions
Medium · window functions · event tables · frame semantics
outbox_event holds event_id, aggregate_type, aggregate_id, aggregate_version, event_type, payload, status ('pending','published','dead'), attempts, created_at, published_at. A projection is missing rows and you must decide whether the relay skipped events or the consumer dropped them. Write three queries over the last seven days: one listing every aggregate_id whose published aggregate_version sequence has a hole, one giving per-day counts with a running total, and one returning the newest published event per aggregate. For each, say where the window function is evaluated relative to WHERE and LIMIT. PostgreSQL 16.
Approach
- Gaps: compute lead(aggregate_version) OVER (PARTITION BY aggregate_id ORDER BY aggregate_version) in a subquery, then filter next_version <> aggregate_version + 1 in the outer query. Window functions are evaluated after WHERE, GROUP BY and HAVING and before the outer ORDER BY and LIMIT, so the predicate cannot sit in the same WHERE clause and PostgreSQL 16 has no QUALIFY.
- Say what the seven-day filter does to the answer: it truncates every partition, so the first row per aggregate has no predecessor inside the window and a hole spanning the boundary is invisible. Widen the window, or join to resource.version as the authority for the true maximum.
- Running total: SELECT date_trunc('day', created_at) AS d, count() AS n, sum(count()) OVER (ORDER BY date_trunc('day', created_at) ROWS UNBOUNDED PRECEDING). An aggregate inside a window call is legal because grouping runs before windowing. The grouping key is unique per row here so ROWS and RANGE agree, but write the frame anyway — over ungrouped rows with tied timestamps the default RANGE frame pulls in every peer row and the total jumps.
- Newest per aggregate: DISTINCT ON (aggregate_id) ... ORDER BY aggregate_id, aggregate_version DESC is the cheap PostgreSQL-only form when an index matches that order; row_number() OVER (PARTITION BY aggregate_id ORDER BY aggregate_version DESC) = 1 is the portable form and needs a subquery for the same evaluation-order reason as the gap query.
Follow-up
- Relay failover redelivers events. Does a duplicate break the gap query, and how would you detect one from this table alone?
- Turn the gap check into a continuous monitor rather than a query someone runs after an incident. What does it watch?
Can you walk us through a complex Apex page or class you developed in a previous role?
Medium · Platform Development & Customization
Approach
- Pick one component you can explain line by line. Open with the business problem, who used it and the data volume. Then walk the layers: the Visualforce page or Lightning component, the controller or extension, a service class for the logic and a selector class for the queries.
- Explain the data binding. In Visualforce, {!...} merge expressions bind to controller getters and setters, action methods handle the postbacks, and view state is capped at 170 KB, so mark fields the page does not need between requests as transient. In LWC, reads use @AuraEnabled(cacheable=true) with @wire, writes use imperative calls, and refreshApex keeps the UI current.
- Show the limit decisions you made. Keep SOQL and DML out of loops, collect related records into a Map<Id, SObject>, and remember that Visualforce iteration components cap a collection at 1,000 items (10,000 with readOnly=true). That cap is the reason to paginate with StandardSetController or a LIMIT/OFFSET pattern.
- Cover security explicitly. Declare the class 'with sharing', enforce CRUD and FLS with WITH USER_MODE or Security.stripInaccessible, and use bind variables (or String.escapeSingleQuotes in dynamic SOQL) so user input cannot inject into a query.
- Close with testing and one trade-off. Write a test class using a data factory, cover bulk cases of 200 records and negative paths, and assert outcomes rather than chasing coverage (75% is the deployment floor, not a goal). Then name one decision you would change today and say why.
Follow-up
- Why a custom controller rather than a standard controller with an extension?
- What happens to the page when the related list returns several thousand records?
- How would you rebuild it as a Lightning Web Component, and what would you reuse?
How do you manage governor limits when writing triggers and batch jobs in Salesforce?
Medium · Platform Development & Customization
Approach
- Name the per-transaction limits that actually break code: 100 SOQL queries synchronous (200 async), 150 DML statements, 50,000 rows retrieved by SOQL, 10,000 rows processed by DML, 10 s CPU synchronous (60 s async) and 6 MB heap synchronous (12 MB async).
- Bulkify every trigger, because a trigger receives records in chunks of up to 200. Collect the Ids into a Set, run one query into a Map, build one List and issue one DML. Use one trigger per object that delegates to a handler class. For recursion, guard with a static Set<Id> of processed records, not a static Boolean, which silently skips the second 200-record chunk of the same transaction.
- For Batch Apex, implement Database.Batchable. start returns a Database.QueryLocator (up to 50 million records). Each execute call gets its scope (default 200, maximum 2,000) with fresh governor limits. Add Database.Stateful only when you need counters across chunks, and use Database.update(records, false) with SaveResult inspection so one bad row does not roll back the whole chunk.
- Keep queries selective. On objects over 200,000 rows, a non-selective query inside a trigger throws a QueryException, so filter on indexed fields (Id, Name, lookups, External Id). Measure headroom with Limits.getQueries() against Limits.getLimitQueries() and the cumulative-limits section of the debug log.
- Move work out of the save transaction when it does not need to finish there: Queueable (it accepts sObjects, returns a job Id and can chain), platform events, or a batch. Triggers cannot make synchronous callouts, and a callout after uncommitted DML in the same transaction throws a CalloutException.
Follow-up
- The trigger works for one record but fails on a 10,000-row Data Loader import; what do you check first?
- When would you choose Queueable over Batch Apex, or over a future method?
- How do you test that the trigger stays within limits under bulk load?
What is your approach to customizing workflows and building integrations within the ServiceNow platform?
Medium · Platform Development & Customization
Approach
- Start with what the platform already provides before writing script: Flow Designer flows, subflows, IntegrationHub spokes and catalog items with variables. Heavily customised baseline records get skipped during upgrades, so configuration keeps upgrades cheaper than custom code.
- For a workflow, set the trigger (record created or updated, with conditions) and put reusable logic in subflows or custom actions. Use the built-in approval actions, and keep sys_ids and URLs in system properties rather than hard-coding them, so the same flow runs in dev, test and prod.
- For outbound integrations, use REST Message records (sn_ws.RESTMessageV2 in script) with credentials held in an OAuth profile or credential alias. Run them asynchronously (async business rule, event or flow) so a slow endpoint never blocks a user's save, and route through a MID Server when the target sits behind a firewall.
- For inbound integrations, use a Scripted REST API or the Table API under an integration user with least-privilege roles. For bulk loads, use import sets with transform maps and coalesce fields so a repeated load updates records instead of duplicating them.
- Explain how you ship and keep it running. Build in a scoped application, move work dev to test to prod with update sets, cover it with Automated Test Framework tests, and log each integration call with a correlation id, retries and an error state someone can act on.
Follow-up
- The external system is down for an hour; what happens to the records that should have been sent?
- When would you write a Script Include instead of a Flow Designer action?
- How do you stop a catalog item change from breaking requests already in flight?
Explain the difference between client-side and server-side scripting in ServiceNow.
Medium · Platform Development & Customization
Approach
- Client-side scripts run in the user's browser on the form: Client Scripts (onLoad, onChange, onSubmit, onCellEdit), catalog client scripts and UI Policies, using g_form and g_user. Use them for user experience: showing, hiding or making fields mandatory and giving instant validation feedback.
- Server-side scripts run on the instance: Business Rules (before, after, async, display), Script Includes, Scheduled Jobs, server-side UI Actions and script steps in flows, using GlideRecord and gs (GlideSystem). They fire however the record changes, whether through the form, a list edit, an import or an API call.
- Bridge the two without hurting performance. Call a client-callable Script Include (one that extends AbstractAjaxProcessor) through GlideAjax with an asynchronous getXMLAnswer callback, and use a display business rule to fill g_scratchpad with data the form needs at load. Avoid GlideRecord in client scripts and synchronous getXMLWait: it blocks the browser and Service Portal does not support it.
- Make the security point. Client-side checks can be bypassed by list edits, imports and web services, so real enforcement belongs on the server: ACLs, Data Policies (the server-side counterpart of UI Policies) and before business rules.
- Know the business-rule timing. In a before rule, set fields on current directly and never call current.update(), which saves again and re-runs the rules. Use after rules for related records, and async rules or events for slow work such as integrations.
Follow-up
- A field is mandatory on the form, but records created through the API arrive without it; why, and how do you fix it?
- When would you use a display business rule with g_scratchpad instead of GlideAjax?
- How would you find which script is slowing down form load?
Given a large dataset of passenger bookings, how would you design an efficient algorithm to find duplicate rec
Medium · Logical Reasoning & Algorithmic Problem
Given a large dataset of passenger bookings, how would you design an efficient algorithm to find duplicate records?
Approach
- Define what counts as a duplicate before choosing an algorithm. Exact duplicates share the same normalised key. Near duplicates are the same passenger and sailing entered twice with typos. Normalise first: trim and case-fold names, strip accents, convert phones to E.164, lowercase emails and parse dates into one format.
- For exact duplicates, hash the normalised key (for example last name + date of birth + sailing + email) into a hash map from key to list of record ids. That is O(n) expected time and O(n) memory. In SQL it is GROUP BY on the key with HAVING COUNT(*) > 1, or ROW_NUMBER() OVER (PARTITION BY key ORDER BY created_at) to keep the earliest row.
- If the data does not fit in memory, partition by hash of the key so all copies land in the same partition and each partition is deduplicated independently (the map-reduce shape). Alternatively, external-sort on the key in O(n log n) and compare adjacent rows.
- For near duplicates, avoid the O(n^2) all-pairs comparison with blocking. Group candidates by a cheap key (sailing plus phonetic code of last name, for example Soundex), compare only within blocks using Jaro-Winkler or Levenshtein on names and exact matches on date of birth, then merge pairwise matches into clusters with union-find.
- Cover the edge cases and the output. Family members can share a surname and address, a parent and child can share a full name (date of birth separates them), and a cancelled-then-rebooked trip is not a duplicate. Return clusters with a survivor rule and a review queue for low-confidence matches rather than deleting rows.
Follow-up
- Two records match on name and date of birth but have different emails; are they duplicates?
- How do you merge a duplicate without breaking payments or other records that reference it?
- How would you measure the precision and recall of the fuzzy matcher?
How would you design a secure, RESTful API to sync guest data between onshore databases and an onboard ship se
Medium · System Integration & Emerging Tech
How would you design a secure, RESTful API to sync guest data between onshore databases and an onboard ship server?
Approach
- Give each side its own database and sync changes rather than whole tables. Each side records writes in a change log or outbox with a monotonically increasing sequence number. The API is GET /changes?since={cursor}&limit=N to pull and POST /changes to push a batch, and the cursor is advanced only after the receiving side commits, so an interrupted transfer simply resumes.
- Make writes idempotent and versioned. Every change carries a unique id so a retried batch is not applied twice. Single-record updates use ETag with If-Match for optimistic concurrency (412 Precondition Failed on a stale version), and deletions travel as tombstones so the other side can remove the record.
- Resolve conflicts by design. Assign field ownership where possible (one side owns the booking data, the other owns what is captured onboard). For fields both sides edit, use a version per record or a hybrid logical clock instead of wall-clock last-writer-wins, since ship and shore clocks can drift; send true conflicts to a review queue.
- Secure it in layers. Use mutual TLS between the ship server and the onshore endpoint, OAuth 2.0 client-credentials tokens scoped per ship, encryption at rest on the ship, field-level encryption for sensitive documents such as passport numbers, only the data that sailing needs, and an audit log of who changed what.
- Design for a thin link. Send compressed deltas in paged batches, put small high-priority changes ahead of bulk ones when the link returns with a backlog, apply backpressure through rate limits, and version the payload schema so a ship running older software can still sync.
Follow-up
- Both sides edited the same guest field while the ship was offline; which value wins and how does the guest's record end up consistent?
- The link returns after days offline with a large backlog; how do you order and throttle the catch-up?
- How would you roll out a schema change when ships upgrade at different times?
What experience do you have working with AI-related tech stacks, and how can machine learning improve predicti
Medium · System Integration & Emerging Tech
What experience do you have working with AI-related tech stacks, and how can machine learning improve predictive maintenance for fleet IT?
Approach
- Answer the experience half honestly. Name what you have actually built or integrated (a hosted model API, a classifier you trained, a retrieval pipeline) and your role in it, and stop at the edge of what you did instead of listing frameworks.
- Frame predictive maintenance as supervised learning: predict whether a device (disk, server, network switch, point-of-sale terminal) fails within the next N days. Build features as rolling-window aggregates of telemetry such as SMART disk counters, temperature, error-log rates, reboot counts and past incident tickets, and take labels from the incident history.
- Get the evaluation right. Failures are rare, so accuracy is misleading; report precision, recall and PR-AUC, and choose the threshold from costs, since a false negative at sea may mean waiting until port for a part while a false positive means an unneeded swap. Split train and test by time, not randomly, and drop any feature recorded after the failure to avoid leakage.
- Choose the model to fit the data. Gradient-boosted trees work well on tabular window features when labels exist. Unsupervised anomaly detection, such as an isolation forest, gives early warnings when labelled failures are too few.
- Plan deployment around connectivity. Train centrally on pooled fleet data, ship a scoring model small enough to run on the vessel's local server so it keeps working offline, turn predictions into actionable tickets (for example a ServiceNow incident or work order), and track drift and the alert hit-rate after launch.
Follow-up
- Failures are rare in the history; how do you build a training set and pick a threshold?
- How do you check that the model is not learning from data recorded after the failure?
- Where does the model run when the ship has no connectivity, and how do you update it?
Exports duplicate a row range about once a week
Hard · leases · clock skew · idempotency
Roughly once a week an export writes a file containing a duplicated range of rows. The affected job_run rows show attempt = 1, status = succeeded, one started_at, and a lease_owner naming a different host from the one whose logs show the job starting. Leases last 30 seconds and are heartbeated every 10 from inside the handler; lease_expires_at is computed on the worker and compared against the database's now(). Find the mechanism, and give a fix that holds even if you cannot fix the clocks.
Approach
- Start from the fact that eliminates the obvious answer. attempt = 1 means no retry was recorded, so this is not a re-run after failure; two workers ran the same row concurrently and the takeover path never touched the counter. lease_owner naming a host other than the one that started the job is the same statement from the other side.
- Enumerate the mechanisms that cause a premature takeover, then find the signal that separates them. Either the lease genuinely expired because the heartbeat did not fire, which is what happens when the heartbeat runs on the handler's own thread and the handler makes a long blocking call, or it only appeared expired because two clocks disagree, since lease_expires_at is written from the worker's clock and evaluated against the database's. The discriminator is the distribution: incidents clustered on the longest exports indict the heartbeat, incidents clustered on one host indict skew. Measure both, and measure each host's offset against the database directly.
- Read the reclaim query precisely. In PostgreSQL now() is transaction start time, not statement time, so a reclaimer holding a long transaction compares against an older timestamp than expected; clock_timestamp() is the statement-time function. This is worth ruling in or out before you redesign anything, because it changes which rows look expired.
- Remove the second clock rather than trying to synchronise it. Issue and extend the lease in the database, with lease_expires_at = now() + interval '30 seconds' in both the claim and the heartbeat, so exactly one clock is ever compared and worker skew stops mattering to this predicate.
Follow-up
- The displaced worker has already streamed half the file to object storage. What makes that side effect safe to repeat?
- You now count takeovers. What alert fires on that counter, and at what threshold?
Suggested practice checklist
The plan covers the loop in the order candidates describe it: logistics and your project story, platform depth for Apex and ServiceNow, algorithms and SQL, integration design, AI and legacy stories, then a full spoken run-through.
Day 1 — Map the loop and choose your project
- Write the five reported rounds on one page and mark the one you are least ready for.
- List the questions to ask the recruiter: team stack, whether a cognitive test or take-home applies, in-office expectations.
- Choose the one Apex or ServiceNow project you will present and list the code and diagrams you can show for it.
Deliverable: A one-page loop map, a recruiter question list and a chosen project with its supporting material.
Day 2 — Apex: project walk-through, limits and async
- Outline the project walk-through for the complex Apex page or class: purpose, structure, data binding, a failure and its fix.
- Write down the synchronous and asynchronous limits for SOQL queries, DML statements, CPU time and heap from Salesforce's documentation, then write a bulk-safe trigger handler.
- Write a one-page comparison of @future, Queueable, Batch Apex and Schedulable with one use case each.
Deliverable: A project outline, a limits table with a bulk-safe trigger handler, and a one-page async comparison.
Day 3 — ServiceNow: scripting scope, workflows and integrations
- Build a two-column table of client-side and server-side scripting in ServiceNow with where each runs and what it can access.
- Outline a workflow customization and an outbound REST integration, including authentication, error handling and retries.
- Write a debugging sequence for a slow production instance, from reproducing the slowness to measuring the fix.
Deliverable: A client versus server table, an integration outline and a written debugging sequence.
Day 4 — Duplicates, SQL tuning and edge-case testing
- Code the duplicate-booking solution in your chosen language with a hash map and then a sorting approach, and state complexity.
- Write the SQL version with GROUP BY and HAVING, then with ROW_NUMBER over a partition, and read an EXPLAIN plan for a slow variant.
Deliverable: Working duplicate-detection code, two SQL versions with a plan reading, and a prioritized edge-case checklist.
Day 5 — Queue, sync API and offline design
- Design the guest notification queue: producers, durable broker, consumers, retries, ordering and delivery guarantees, and what happens when a ship is offline.
- Design the secure REST sync API between onshore databases and a ship server: authentication, versioned resources, idempotency keys, batching and error taxonomy.
Deliverable: Two design write-ups (notification queue and sync API) and a one-page offline sync answer.
Day 6 — AI question and legacy integration story
- Write a two-minute answer on your AI-related experience and how machine learning could support predictive maintenance for fleet IT, naming the data you would need and how you would check a model's value.
- Outline how a model could categorize incoming ServiceNow tickets, including where it is called and what happens on low confidence.
- Write the legacy-to-cloud integration story in situation, decision, risk, result form with measured outcomes.
Deliverable: A spoken-length AI answer, a ticket-categorization outline and a written integration story.
Day 7 — Full spoken run-through for the panel
- Answer aloud to a blank screen: the project walk-through, the offline sync design and the legacy integration story.
- Do a timed mixed set of one coding problem, one SQL tuning question and one Apex or ServiceNow question.
- Prepare your questions for the recruiter and the team about their stack and onshore versus onboard systems.
Deliverable: Recorded spoken answers for three panel topics and a list of questions to ask.
Project stories and integration experience
Candidates report that the panel covers behavioral scenarios, and the role involves working with product managers, business analysts and other engineering teams. Prepare stories where you made the call, showed the numbers, and handled a disagreement or an outage. The reported prompts below are partly technical in subject, so each needs a concrete project behind it.
- Walk me through how you would test your code for edge cases under a tight deadline.
- Tell me about a time you turned a vague business requirement from a product manager or analyst into a technical design.
- Describe a production performance problem you debugged, how you found the cause and how you proved the fix.
How do you handle asynchronous processing in Apex to ensure platform performance is not degraded?
Medium · Platform Development & Customization
Approach
- Start from why asynchronous work exists: a synchronous Apex transaction allows 100 SOQL queries, 10 seconds of CPU and 6 MB of heap, while asynchronous contexts allow 200 queries, 60 seconds and 12 MB. Moving heavy work off the user's transaction keeps the page responsive, and each asynchronous job gets fresh limits, but it does not remove limits.
- Choose the tool by the job. @future suits a simple fire-and-forget callout with primitive parameters. Queueable accepts sObjects and complex types, returns a job Id and can chain a follow-up job. Batch Apex (start, execute, finish; default scope 200, set through the scope size argument) processes very large record sets through a QueryLocator. Schedulable starts work at a time. Platform Events decouple publisher from subscriber.
- Keep the trigger bulk-safe: collect record Ids across the whole trigger context, enqueue one job per transaction rather than one per record, guard against recursion, and avoid calling @future from a future or batch context.
- Make the job safe to retry: re-query records by Id inside the job because data may have changed, use Database.update(records, false) and log each SaveResult so one bad row does not roll back the scope, use a Queueable Finalizer for failure handling, and add Database.Stateful only when you need state across batches.
- If the question is taken as a story, give a measured example: what was slow (CPU time or lock contention on a trigger), what you moved async and why that tool, and the before and after you saw in the Apex Jobs page or debug logs.
Follow-up
- Why Queueable instead of @future for that job?
- A batch job fails in one scope; what happens to the other scopes and how do you retry?
- How do you stop a trigger from enqueuing a job for every record in a bulk load?
How do you handle data synchronization issues when a ship loses satellite connectivity and goes offline?
Medium · System Integration & Emerging Tech
Approach
- Open with the setting and the failure mode: which data lived on both sides, and what went wrong when the link dropped (stale profiles aboard, or duplicate writes after reconnect). If you have no maritime experience, use any intermittently connected system, such as a mobile app or a branch or point-of-sale terminal, and say so.
- Describe the mechanism you owned: write locally first, record each change in a durable outbox with a per-source sequence number, send in batches, and advance an acknowledged cursor only when the other side confirms. Replay must be harmless, so every change carries an idempotency key (record id plus version) that the receiver deduplicates on.
- State the conflict rule per entity. Last-writer-wins on wall-clock time is lossy and clocks drift, so use version numbers or vector-style versions and a field-level merge where fields are independent. Say which side owns which data, for example shore for identity data and ship for onboard purchases.
- Cover reconnect behavior: exponential backoff with jitter, resumable batches, small high-priority changes first, a dead-letter queue for messages that keep failing, lag alerts, and a periodic reconciliation comparing counts or checksums by partition to catch silent drift.
- Close with a result you measured (lag after reconnect, conflicts per day, duplicates prevented) and one thing you would change. The mistake that sinks this answer is saying "I used a queue" with no conflict rule and no replay story.
Follow-up
- Two sides edit the same guest profile while offline; which edit wins and why?
- How do you confirm ship and shore agree after reconnecting?
- What changes if the outbox grows for several days before the link returns?
Describe a time you had to integrate a legacy third-party system with a modern cloud platform.
Medium · System Integration & Emerging Tech
Approach
- Set the scene in two or three sentences: what the legacy system was (SOAP service, flat-file drops, database views), what the modern platform needed from it, and what you personally owned. Interviewers need to hear your decision, not the team's.
- Explain the structure you chose and why: an adapter or anti-corruption layer that translates the legacy contract into a clean internal one, instead of point-to-point calls that leak legacy quirks into the new platform. Say whether the interface was synchronous API, scheduled batch or event, and which legacy constraint drove that choice (no changes allowed, throughput limit, authentication).
- Name the risks you handled: field mapping and encoding differences, retries made idempotent, rate limits, error handling and alerting, and how you tested against a system you could not change (recorded fixtures, contract tests, a sandbox copy).
- Describe the rollout: run in parallel with comparison of outputs, cut over in stages, keep a rollback. If there was a disagreement, such as wrap versus rewrite, say how you settled it with a measurement or prototype rather than seniority.
- End with outcomes you actually measured, the incident or surprise that taught you something, and what you would do differently. The common failure is describing the cloud product's features instead of your decisions.
Follow-up
- How did you test against a legacy system you were not allowed to change?
- How did you cut over without downtime or data loss?
- What would you do differently if you ran that integration again?