Interview guides · Collinson
Collinson Software Engineer Interview Guide
Collinson Software Engineer interview guide: three reported rounds, Java and API questions, take-home prep, behavioral stories and a 7-day plan.
The 60-second brief
Candidate reports place Collinson in loyalty and travel technology, building platforms used by global travelers and loyalty program members. The role description mentions services that handle a high volume of customer interactions and work that connects older systems to cloud-based ones. A Software Engineer there is described as building APIs, full-stack applications and system components, joining code reviews, sprint planning and stand-ups, diagnosing production issues and writing technical documentation. The listed requirements are Java or a similar backend language, API development and Git; AWS or Azure, CI/CD pipelines and front-end frameworks are listed as extras.
This guide is for candidates applying as a Software Engineer at Collinson, especially backend or full-stack engineers who work in Java.
Candidates report three rounds over roughly 3-5 weeks: an Initial Screening Call with a recruiter, Technical Assessments that may include take-home assignments, and Interviews with Managers. Some stages may also include psychometric or arcade-style cognitive tests.
Focus areas
- Full-Stack Development
- Technical Interview Problem Solving
- Take-Home Assessments
Overview
The work candidates describe is practical engineering: REST APIs in Java, full-stack features, integration between older and newer systems, and keeping production services healthy. That shapes preparation. Reading about design patterns is less useful than having recently built something small end to end, so you can talk about module layout, error handling and security from fresh experience rather than memory.
The reported questions split evenly. Five are technical discussions: your Java and API experience, debugging a production issue in a distributed system, trade-offs between database architectures under high concurrency, keeping a large codebase maintainable, and how you decide when to adopt new technology. Five are behavioral: a disagreement with a product manager or teammate, tight deadlines or shifting requirements, taking over a failing or stalled project, giving and receiving code review feedback, and why Collinson. Each needs a prepared structure, and the behavioral five need real stories drawn from your own projects.
Candidates also report a few practical points. The technical stage may be a take-home, with example scenarios such as building a full-stack feature from a requirements document or refactoring a slow snippet. Some stages may include psychometric or arcade-style cognitive tests, so plan a quiet room and a working keyboard. The advice that recurs is to ask clarifying questions before solving, to say out loud why you chose one approach over another, and to know every project on your CV in detail.
The Collinson interview process
Candidates describe three stages, from a recruiter call through technical assessments to manager interviews. Order, format and the number of technical sessions can differ by team and region.
Initial Screening Call
Candidates describe the Initial Screening Call as a conversation with a recruiter about your background and fit for the role. Treat it as the first test of how clearly you can explain your own work: the recruiter will not be reading your code, so your account of Java and API projects has to stand on its own. It is also the moment to ask what the later stages contain, including whether a take-home or cognitive tests are part of the loop.
What to demonstrate
- How well your background lines up with a Java and API-focused engineering role
- Whether you can describe your projects and your part in them concisely
- Your reasons for wanting this company and role
How to prepare
- Write a short walk through your CV that spends most of its time on your Java and API projects and what you personally decided
- Prepare a specific answer to the question about what draws you to Collinson, built on its loyalty and travel context
- Have logistics ready (notice period, location, availability) and a short list of questions about the next stages, including take-home and online tests
Technical Assessments
Technical Assessments are reported as one or more technical evaluations that may include take-home assignments. Candidates list code structure, API design (RESTful practice and security) and error handling as the areas, with example scenarios of building a full-stack feature from a requirements document and refactoring an inefficient snippet. A coding-style stage is where working code, tests and written reasoning matter, and it is also where the technical discussion topics (production debugging, database trade-offs, code quality) are practised. Read the requirements twice, ask about anything ambiguous, and keep a short note of your decisions.
What to demonstrate
- Working code that meets the stated requirements
- How you organise modules and components
- REST API design, including security basics
- Error handling that fails gracefully
How to prepare
- Build a small Java REST service from a written spec with validation, a consistent error body, authentication and tests, then add a minimal front end to it
- Refactor a deliberately slow snippet (repeated scans, N+1 queries, wrong collection type) and write down the before and after cost
- Practise explaining your design choices out loud, including the database trade-offs for high-concurrency use and your approach to debugging a production fault
- Set up a quiet space and test your keyboard in case a stage includes psychometric or arcade-style cognitive tests
Interviews with Managers
Interviews with Managers is described as a series of interviews with engineering managers and team members about your skills and how you would work with them. Candidates also list conflict resolution, project ownership and growth (learning from mistakes, mentoring junior developers) as areas to be ready for. Expect to be asked about projects on your CV in detail, including the architectural trade-offs you made, so rehearse stories with concrete decisions and results rather than general principles.
What to demonstrate
- How you handle disagreement and shifting requirements in a team
- Accountability for a project from start to deployment
- How you learn from mistakes and help others improve
- Your ability to discuss past architectural decisions and their trade-offs
How to prepare
- Build a bank of four or five real projects and map each to several prompts: disagreement, deadline pressure, a stalled project, code review
- For every project, write the decision you made, the alternative you rejected, and the measurable result
- Prepare an honest example of a mistake, what changed afterwards, and a time you helped a less experienced colleague
- Prepare questions about team practices such as code review, release process and on-call that you actually want answered
Where candidates lose points
Describing Java and API experience as a list of frameworks and annotations with no decisions behind them
For each project, state one design choice, the alternative you rejected and the result: for example offset versus keyset paging, synchronous call versus queue, or an idempotency key on a retried POST.
Submitting a take-home that runs but ignores part of the requirements document, has no tests and does not record assumptions
Turn the requirements into a checklist before coding, ask about anything ambiguous, cover error handling and authentication, and add a short README with your decisions and what you would do next.
Answering the database trade-offs question as a product comparison (SQL versus NoSQL) rather than starting from the access pattern
Begin with the read/write mix, contention hot spots and consistency needs, then compare a single relational primary, read replicas, sharding and caching by what each costs and where it can serve stale data.
Jumping straight to a fix when asked about debugging a production issue in a distributed system
Walk through a sequence: confirm impact, check what changed, read logs, metrics and traces to localise the failing service, mitigate (rollback or flag) first, then find the root cause and add a regression test.
Telling a behavioral story in which your own role is hidden behind the team, or the other person is the villain
Name what you decided and did, include what the other side wanted, and finish with the result and what you would change. Prepare each story's numbers and facts beforehand.
Questions & practice
Merge partitioned event streams into one ordered feed with bounded lateness
Hard · k-way merge · watermarks · out-of-order streams
The read-model service consumes 64 log partitions carrying about 4,000 events per second in total. Each partition is ordered within itself, but partitions drift by up to 30 seconds, and the activity feed must present a tenant's events in occurred_at order. Produce the merge. State its complexity, the buffer it requires in events and in bytes, what happens when one partition is idle, and what you do with an event that arrives after you have already emitted its position. Payloads average 1 KB.
Approach
- Merge with a min-heap over the 64 partition heads keyed on (occurred_at, event_id): O(log P) per event and O(n log P) overall. The tie-break on event_id is what makes the output deterministic when two partitions carry the same millisecond, which matters because the feed is paginated and a non-deterministic order reorders pages under the reader.
- Emitting the heap head is only correct once every partition has produced everything up to that timestamp, so the emit condition is a watermark: the minimum across partitions of the highest occurred_at seen, less the allowed lateness. Events are held until the watermark passes them, which is what turns individually ordered streams into a jointly ordered one.
- Size the buffer from the lateness rather than guessing: 4,000 events per second times 30 seconds is 120,000 buffered events, and at 1 KB each about 120 MB of heap. That number is the real price of the ordering guarantee and belongs in front of whoever asked for it.
- Handle the idle partition explicitly, because it fails the feed rather than corrupting it: a partition with no traffic never advances its own maximum, so the watermark freezes and output stops entirely. Either every partition emits a periodic idle marker carrying the broker's current time, or the watermark falls back to wall clock for a partition silent beyond a threshold.
Follow-up
- The lateness budget is raised to five minutes. What is the new buffer, and what besides memory changes?
- The consumer restarts. Where does it resume from, and what does the feed look like for the first 30 seconds?
Canonicalise a request body into a stable idempotency fingerprint
Medium · parsing · canonicalisation · hashing
idempotency_key.request_fingerprint is a SHA-256 over the method, path and canonicalised body, and a retry whose fingerprint differs must be rejected with 422 rather than served the stored response. Write the canonicaliser. Bodies are JSON up to 256 KB nested at most 32 levels; clients vary key order, whitespace and unicode escaping, and some send 64-bit ids as JSON numbers. Produce a deterministic byte string such that semantically identical bodies match and any semantic difference does not. State your complexity and name two normalisations you refuse to perform.
Approach
- Parse once into a tree, then re-serialise under fixed rules: object keys sorted, array order preserved, one escaping convention, no insignificant whitespace. Parsing is O(n) and sorting keys is O(k log k) per object, so O(n log n) overall with O(depth) stack, and the 32-level cap is enforced during parsing because hostile nesting is how a canonicaliser becomes a stack overflow.
- Sort keys by their UTF-8 bytes and say why the obvious implementation is wrong in some runtimes: a default string comparison that orders by UTF-16 code units places surrogate pairs, meaning code points from U+10000 up, below U+E000 to U+FFFF, which is not UTF-8 byte order, so two services written in different languages disagree on the same document.
- Do not re-encode numbers through a double. IEEE-754 binary64 represents integers exactly only up to 2^53, so normalising a 19-digit id through a float changes it, and 1 against 1.0 cannot be reconciled without deciding whether they are the same value. Preserve the literal token, and require ids as strings at the API boundary if you want them comparable.
- Reject duplicate keys rather than picking one. JSON permits them and parsers disagree, most keeping the last, so any choice you make ties the fingerprint to a parser detail that the code handling the request does not necessarily share.
Follow-up
- A client sends the same logical request with an extra field your API ignores. Same key, different fingerprint, so you return 422. Is that the right answer?
- Where does the fingerprint get computed relative to request decompression and the body-size limit?
Archive a resource graph without breaking live references or recursing
Medium · graph traversal · topological order · tenant isolation
Resources reference other resources within a tenant; for the largest tenant the reference table holds up to 2,000,000 nodes and 8,000,000 edges. Archiving a resource must archive everything reachable from it that nothing outside the set still references, refuse when a live external referrer exists, and terminate when references form cycles, which they legitimately do. Produce the archive order and the refusal list, targeting O(V+E). Say what stops the traversal crossing a tenant boundary, and why recursion is the wrong control structure at this size.
Approach
- Load the subgraph with the tenant predicate on both endpoints of the edge, not only on the side you started from. Scoping the left table alone is the classic cross-tenant leak: one mis-entered edge then pulls another tenant's resources into the traversal and, worse, into the archive.
- Traverse iteratively with an explicit stack. A 2,000,000-node graph can hold a chain deep enough to exhaust a native stack in the low tens of thousands of frames, and that failure is a process crash rather than an error you can return.
- Treat cycles as data rather than corruption: compute strongly connected components with Tarjan in O(V+E) using its own explicit stack, then condense. The condensation is a DAG, so a topological order over it gives the archive order, and every member of a component archives in one transaction because no order within a cycle is valid.
- Decide refusals with reverse edges. A candidate is archivable only if every in-edge originates inside the candidate set, so build the transpose or count in-degrees restricted to the visited set, and emit each blocked resource with the id of the external referrer, which is the only part of the answer an operator can act on.
Follow-up
- The graph is read in one query and the archive writes a minute later. What can change in between, and how do you make the write safe?
- The candidate set is 400,000 resources. Is that one transaction, and if not, what does a half-finished archive look like to a reader?
Replace offset paging on the resource feed with keyset
Easy · keyset pagination · composite index · sort order
resource holds resource_id, tenant_id, owner_user_id, title, body_ref, version, status ('draft','active','archived','deleted'), created_at, updated_at, deleted_at, with an index on (tenant_id, status, updated_at DESC, resource_id DESC). The listing endpoint returns active resources for one tenant, newest update first, 50 per page, today with LIMIT 50 OFFSET n. Tenants reach page 400 and rows are created while they read. Write the keyset query, define what the cursor carries and how it is encoded, and say which part of the index each predicate uses. Assume PostgreSQL 16.
Approach
- Name the two failures separately. OFFSET 20000 makes the server produce and discard 20,000 rows, so page cost grows with depth rather than with page size. Independently, any write that changes how many rows sort above the offset moves the window between two fetches, and the direction decides which anomaly you get: an insert lands at the head of updated_at DESC and pushes already-returned rows down past the boundary, so they are returned a second time; a delete above the offset, or a row whose updated_at is bumped above the cursor, pulls rows up and one is never returned at all. Nothing in the response reveals either.
- Write the seek: WHERE tenant_id = $1 AND status = 'active' AND (updated_at, resource_id) < ($2, $3) ORDER BY updated_at DESC, resource_id DESC LIMIT 50. The row-value comparison is one index range rather than a disjunction, and both columns are NOT NULL, which is what makes that comparison well defined.
- Map each predicate onto the index: tenant_id and status are equality on the leading columns, (updated_at, resource_id) is the range, and the ORDER BY matches the index order so no Sort node appears and the scan stops after 50 rows. The DESC in the definition only matters for mixed directions — a plain ascending btree on the same columns is read backwards for this query.
- Put both sort columns in the cursor and nothing the client can tamper with into another tenant: base64 of (updated_at, resource_id), validated server-side, with tenant_id taken from the principal.
Follow-up
- The client asks for 'jump to page 400'. What do you offer instead, and what does the honest version cost?
- Sort order becomes user-selectable across four columns. How many indexes is that, and which would you refuse to add?
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?
Explain the trade-offs between different database architectures for high-concurrency applications.
Medium · Technical and Domain Knowledge
Approach
- Start from the workload: read/write ratio, whether writes contend on the same hot rows (a balance, an inventory counter), and which data must never lose an update versus which can be served slightly stale. The answer changes the architecture.
- Single-primary relational (PostgreSQL, MySQL): ACID transactions, and MVCC so readers do not block writers. Read replicas scale reads, but asynchronous replication brings replica lag and read-your-writes problems, and all writes still go through one primary.
- Concurrency control on hot rows: pessimistic locking with SELECT ... FOR UPDATE versus optimistic locking with a version column and UPDATE ... WHERE id = ? AND version = ?. Know the isolation levels: PostgreSQL defaults to Read Committed and MySQL InnoDB to Repeatable Read; Serializable needs retry logic for serialization failures.
- Scaling writes: sharding by a partition key spreads the load, but costs you cross-shard transactions (two-phase commit or sagas), scatter-gather queries, hot keys and painful resharding. Key-value and wide-column stores (DynamoDB, Cassandra) give high write throughput, but you can only query efficiently along the key design. Cassandra's tunable consistency is strong when R + W > N.
- Distributed SQL (Spanner, CockroachDB) keeps ACID across nodes through consensus replication (Paxos/Raft), and you pay in write latency. Frame it with CAP/PACELC (during a partition, consistency or availability; otherwise, latency or consistency) and mention supporting pieces: connection pooling (HikariCP, PgBouncer) and cache-aside caching with an invalidation plan.
Follow-up
- Two requests try to spend the same balance at the same moment. How do you prevent a double spend in your chosen design?
- A user updates a record and the next page load shows the old value because it read from a replica. How do you fix that?
- When would you shard a relational database yourself rather than move to a distributed SQL database?
What is your process for ensuring code quality and maintainability in a large codebase?
Medium · Technical and Domain Knowledge
Approach
- Automate the baseline so review time goes to design: CI on every pull request runs the build, unit and integration tests, formatting and static analysis. For Java that could be Spotless or Checkstyle for style, SpotBugs, PMD or SonarQube for bug patterns, and JaCoCo for coverage, with the merge blocked on failure.
- Use a testing pyramid: many fast unit tests, fewer integration tests against real dependencies (for example Testcontainers with the production database engine), a thin end-to-end layer, and contract tests on APIs shared between services. Treat coverage as a signal, not a target.
- Set review norms: small, single-purpose pull requests, a short checklist (error handling, tests for the change, backward-compatible API changes, naming) and CODEOWNERS for sensitive areas. Give one concrete example of a review comment that prevented a defect.
- Keep the structure enforced: clear module boundaries and dependency direction (layered or hexagonal), checked automatically with a tool such as ArchUnit, and design decisions recorded in short architecture decision records. Refactor legacy areas incrementally behind tests, for example with the strangler fig pattern.
- Manage debt visibly: track flaky tests, build time and escaped defects, agree debt work with the product owner in sprint planning, and close with a measured before/after from a refactor you led.
Follow-up
- How do you get a legacy module with no tests under test before you change it?
- A lint or coverage rule is slowing the team down. How do you decide whether to keep it?
- What do you do about a test suite that fails intermittently?
How do you stay updated with emerging technologies and decide when to implement them?
Medium · Technical and Domain Knowledge
Approach
- Name concrete sources rather than "I read blogs": release notes for the tools you ship with (for example what each Java LTS release added), the changelogs of your frameworks, a technology radar, conference talks, and a small hands-on spike when something looks relevant to your work.
- Start from a measured problem, not the technology: say which pain the new tool would remove, then weigh maturity, community and vendor support, licensing, security posture, the team's skills, operating cost and how hard it would be to back out.
- Prove it small: run a time-boxed spike against a success metric agreed in advance, pilot it on a low-risk service behind a feature flag with a rollback plan, and write down the alternatives and the rationale in an architecture decision record.
- Ground it in one real example with its caveats, such as moving a blocking, I/O-bound Java service to virtual threads (final in Java 21): you get far more concurrent requests per host, but you must not pool virtual threads, should watch heavy ThreadLocal use, and on JDK 21 pinning inside synchronized blocks can erase the benefit (JDK 24 largely fixed this).
- Include a case where you decided not to adopt something, and how you spread adoption when you did (docs, pairing, a migration guide). That shows judgment, not just enthusiasm.
Follow-up
- Tell me about a technology you evaluated and rejected. What tipped the decision?
- How would you convince a team that is reluctant to move off a familiar tool?
- After adoption, how did you measure whether it paid off?
How do you approach debugging a production issue in a distributed system?
Medium · Technical and Domain Knowledge
Approach
- Mitigate before diagnosing: size the blast radius from dashboards (error rate, p50/p99 latency per service, region, version, tenant), and if onset lines up with a deploy or config change, roll back or turn off the feature flag first, then find the root cause.
- Build a timeline: line up the first bad minute with deploys, config pushes, dependency incidents and traffic spikes, and check whether every instance is affected or only a subset (one zone, one node, one tenant). A subset points at state or infrastructure, not code.
- Follow one failing request across hops with a correlation or trace ID (OpenTelemetry spans, structured logs keyed on request id) to find the hop that adds latency or returns the error. Remember clock skew between hosts when ordering log lines from different machines.
- Test the usual distributed failure modes one at a time: retry storms amplifying load, a caller timeout shorter than the callee's, exhausted connection or thread pools, long GC pauses in a Java service (GC logs, thread dumps via jstack), stale caches, and non-idempotent retries creating duplicates.
- Close the loop: ship the fix, confirm the metric recovers, add the alert or test that would have caught it earlier, and write a blameless postmortem with owners for each action item.
Follow-up
- How would you debug an issue that only shows up in production and you cannot reproduce locally?
- A downstream call times out only at p99 and only some of the time. What do you check first?
- How do you stop client retries from turning a partial outage into a full one?
Suggested practice checklist
The plan follows the three reported rounds: it starts with the recruiter-call material, spends the middle days building the Java API and full-stack work that the technical assessments describe, then rehearses the discussion and behavioral questions.
Day 1 — Map the loop and write your project walkthroughs
- Write out the three reported rounds (Initial Screening Call, Technical Assessments, Interviews with Managers) and mark which one you are least ready for.
- List the projects on your CV that involved Java or APIs and write a spoken walkthrough for the strongest two: what you owned, one design choice, one failure and its fix.
- Write and practise your answer to what draws you to Collinson, using public information about its loyalty and travel work.
Deliverable: A one-page loop map, two written project walkthroughs for the Java and API question, and a written answer to the why-Collinson question.
Day 2 — Build a Java REST service from a spec
- Write a short requirements document for a small service (for example bookings or points balances) and implement it in Java with validation, a uniform error body, authentication and unit and integration tests.
- Add an idempotency key to the create endpoint and test that a repeated request gives the same result.
Deliverable: A tested Java service in a Git repository with an error contract, an idempotent create endpoint and a worked fingerprint exercise.
Day 3 — Rehearse the full-stack take-home
- Take a new written requirement and build a thin vertical slice: API, persistence and a simple front-end page, with small, meaningful Git commits.
- Write a README listing assumptions, trade-offs, what is tested and what you would improve; run the tests in a CI workflow if you can.
- Review your code against a checklist: module boundaries, input validation, authentication, error handling and logging.
Deliverable: A working full-stack slice with commit history, a README of decisions and a self-review checklist.
Day 4 — Refactor for performance and data-layer trade-offs
- Take a slow snippet (a nested loop, an N+1 query, a list used for lookups) and refactor it, recording the complexity before and after.
- Say out loud, then write down, your answer to the database trade-offs question for a high-concurrency workload: single primary, read replicas, sharding, caching and the consistency each gives up.
Deliverable: A refactored snippet with measured improvement, a keyset paging solution and a written database trade-offs answer.
Day 5 — Production debugging, code quality and technology choices
- Write a step-by-step incident checklist for a distributed system (impact, recent changes, logs, metrics, traces, mitigation, root cause, follow-up) and apply it to a failure you have handled.
- Write your approach to code quality in a large codebase: review practice, tests, linters, documentation and dependency hygiene.
- Write how you follow new technology and the criteria you use before adopting it in a production system.
Deliverable: An incident checklist, a code-quality answer, and a technology-adoption answer, each short enough to say aloud.
Day 6 — Behavioral story bank
- Write a story for each of the disagreement, tight-deadline, stalled-project and code-review prompts in a situation, action and result outline, with one fact or measurement in each.
- Add one story about a mistake and one about mentoring someone, since growth is a reported area.
- Say each story aloud and trim it until your own decision and the outcome come first.
Deliverable: Five rehearsed stories mapped to the reported behavioral questions, each with a stated result.
Day 7 — Full dry run
- Run the loop in order: a recruiter-style walkthrough of your CV, a build-or-refactor task from a fresh requirement, then a mixed set of technical discussion and behavioral questions answered aloud.
- Review where you lost clarity and tighten those answers; list the questions you want to ask managers and team members.
Deliverable: A completed mock in loop order, revised weak answers and a written list of questions to ask.
Stories and answers for the manager interviews
Candidates report behavioral questions on conflict with a product manager or teammate, deadlines and shifting requirements, rescuing a stalled project, code review and motivation for joining. They also name project ownership from start to deployment and growth, including mentoring junior developers, as areas to cover. Build a bank of four or five real projects and map each to several prompts, so every answer has a decision and a result.
Can you walk us through your experience with Java and API development?
Medium · Technical and Domain Knowledge
Approach
- Choose one or two projects you can describe in depth and open with what the service did, who called it and what you personally owned. Then go layer by layer: resource design (nouns, HTTP verbs, status codes), request validation, a consistent error body, authentication and authorisation, pagination and versioning.
- Name the Java pieces you actually used and the decision behind each: the framework, how you handled concurrency or thread safety, how you modelled data (immutable DTOs versus entities), and how you tested (unit tests for logic, integration tests against a real database or test container).
- Cover reliability: timeouts and retries on outbound calls, idempotency for POST or payment-like operations (an idempotency key), and what the API returns when a dependency fails. A concrete incident or bug you fixed is more convincing than a list of annotations.
- End with a trade-off and a retrospective, for example why you chose synchronous REST over a queue for one flow, and what you would change now. Avoid a pure technology list; every item should come with a reason.
Follow-up
- How did you version the API or change a response without breaking existing clients?
- A client retries a request after a timeout. How do you stop a duplicate write?
- Which part of that Java code would you refactor today, and why?
Describe a time you had to manage a disagreement with a product manager or team member.
Medium · Behavioral and Leadership
Approach
- Use a situation-task-action-result outline and choose a disagreement about substance (scope, priority, technical approach, a release date), not about personality. Give the two positions fairly in a sentence each, including the other person's real constraint, such as a launch date or a customer commitment.
- Show what you did to resolve it with evidence rather than volume: a small prototype, a measurement, a list of risks with costs, or a smaller version of the feature that met both goals. Say how the decision was finally made and who made it.
- State the result and the relationship afterwards. If you lost the argument, say how you committed to the decision and what you did to reduce its risk. If you won, say what you conceded.
- The mistake that sinks this answer is a story where the other person is plainly wrong and you were plainly right. Show that you understood their goal and changed something because of it.
Follow-up
- What if the product manager had overruled you and the decision turned out to be wrong?
- What did you learn about that person's priorities that you used later?
- How did you keep working together after the disagreement?
How do you handle tight deadlines or shifting project requirements?
Medium · Behavioral and Leadership
Approach
- Pick one real project where both pressures appeared. Describe the deadline or the change in requirement, then show a method: separate must-have from nice-to-have with the stakeholder, slice the work vertically so something usable ships early, and put unfinished parts behind a feature flag.
- Explain how you communicated. Raise the risk as soon as you saw it and offer options (cut scope, move the date, add people), rather than announcing a miss on the last day. Mention a written record of requirement changes, so later disagreement is about facts.
- Say what you protected under pressure: tests on the critical path, error handling, and a rollback plan. Be explicit about any shortcut you took on purpose and how you logged it and paid it down.
- Avoid a hero story of long hours as the whole answer. The point is judgment about what to cut and how you kept stakeholders informed.
Follow-up
- Which feature did you cut, and who agreed to that?
- What technical debt did you take on, and did you pay it back?
- How do you handle a requirement that changes after the code is already in review?
Tell us about a time you took ownership of a project that was failing or stalled.
Medium · Behavioral and Leadership
Approach
- Start by explaining why the project was failing: unclear scope, a blocked dependency, no clear owner, a technical risk nobody had tested, or constant changes in direction. Show how you found out, for example by talking to each contributor and reviewing the backlog and the code state.
- Describe the reset: write down the current state, agree a single goal and the smallest next milestone, assign owners to the remaining work, and remove the top blocker yourself or by escalating with a specific request. Make progress visible with a short regular update.
- Give the outcome with numbers or facts, such as what shipped, what was dropped and how stability changed. If the honest result was stopping the project, say how you reached that decision and who agreed.
- Be specific about your part. Saying that you pushed everyone to deliver is vague; saying you split the work into three pieces, took the integration piece, and agreed a cut with the stakeholder is checkable.
Follow-up
- How did you decide the project was worth rescuing instead of stopping it?
- What authority did you have, and who did you need to bring in?
- What would you have noticed earlier to prevent it stalling?
How do you provide and receive constructive feedback during code reviews?
Medium · Behavioral and Leadership
Approach
- Answer both halves with habits and one example each. As a reviewer: keep pull requests small, check behavior, tests, error handling, API compatibility and security first, leave style to formatters and linters, and mark each comment as blocking or optional. Ask a question when unsure and explain the reason behind a request.
- Phrase feedback about the code, not the person, and offer an alternative or a short snippet. Mention that you read the whole change for intent before commenting, and that praise for a good solution is part of review too.
- As an author: write a clear description of what changed and why, keep the diff reviewable, and respond to every comment. When you disagree, answer with evidence such as a benchmark or a failing test, and accept the point when you cannot disprove it.
- Include a real moment where a review caught a defect in your own code or where you changed how you work because of a comment. This covers the growth side without sounding rehearsed.
Follow-up
- How would you give feedback on a design flaw in a much more senior colleague's change?
- Tell me about a review comment you disagreed with and how it ended.
- What do you do when a pull request sits waiting for review?
What draws you to the mission of Collinson specifically?
Medium · Behavioral and Leadership
Approach
- Do your research before the call and then answer from it. Read Collinson's public materials and note what it does in loyalty and travel, and what kind of platform that implies for an engineer: customer-facing APIs, high traffic, integrations and moving older systems to the cloud.
- Link that to your own experience in two or three sentences, for example work on high-volume APIs, integrations or reliability that makes the problem space familiar. Specific connections to a real project carry more weight than praise.
- Add a forward-looking point about what you would want to work on or learn there, framed around the role as candidates describe it (APIs, full-stack features, production support).
- Do not recite a mission statement or make claims about the company that you cannot source. If you do not know something, say so and turn it into a question for the team.
Follow-up
- What do you know about how loyalty programs or travel platforms work, and where does engineering matter most?
- What would you want to learn or build in your first months?
- Why this role rather than similar roles at other companies?